Getting the IHostEnvironment in .NET Core 3.0

Standard

Where as .NET Core 2.1 introduced the GenericHost concept to aid in constructing non-HTTP applications while leveraging primitives for DI, logging, and config, .Net Core 3.0 focused on restructuring the web hosting to be compatible with the generic host instead of having to duplicate code in multiple abstractions.
I may go into the reasons for this change in another post but as I was recently asked the question, I thought I would provide a simple example of how to leverage the IHostedEnvironment in .NET Core 3.0

First add the following 2 packages to your application:
dotnet add package Microsoft.Extensions.Hosting
dotnet add package Microsoft.Extensions.DependencyInjection

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace TestIHostEnvironment
{
    class Program
    {
        static void Main(string[] args)
        {
            var host = Host.CreateDefaultBuilder()
                .ConfigureServices((context, services) =>
                {
                    services.AddTransient<ITestService, TestService>();
                })
                .Build();

            using (var serviceScope = host.Services.CreateScope())
            {
                var serviceProvider = serviceScope.ServiceProvider;
                var testService = serviceProvider.GetRequiredService<ITestService>();

                var result = testService.DoStuff();

                Console.WriteLine(result);
            }
        }
    }
}

public interface ITestService
{
    string DoStuff();
}

public class TestService : ITestService
{
    private readonly IHostEnvironment _iHostedEnvironment;

    public TestService(IHostEnvironment iHostedEnvironment)
    {
        _iHostedEnvironment = iHostedEnvironment;
    }

    public string DoStuff()
    {
        return $"Stuff Done in {_iHostedEnvironment.EnvironmentName} environment";
    }
}

Inserting an Element into Web.Config or XML file using Powershell

Standard

The following is an overview on how to inject a new XML tag into an XML file at a specific location using a Powershell script. The functionality is often used during automated deployment but has several uses. There are several methods for accomplishing this, however I found the following to be most compatible with several versions.

Function AddClientToXml 
{
    [CmdletBinding()]
    param(
       [Parameter(Mandatory=$true)]
       $configFilePath 
    )
    # Load the web.config file into an XML document.
    $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($configFilePath)

    # Create our XML to be inserted
    $clientXml = @"
        <client>
            <endpoint address="RegistrationAddressValue"
                behaviorConfiguration="SampleClientCertificateBehavior" binding="wsHttpBinding"
                bindingConfiguration="WSHttpBinding_RegistrationService"
                contract="Registration.RegistrationService" name="WSHttpBinding_RegistrationService">
                <identity>
                    <dns value="RegistrationDNS" />
                </identity>
            </endpoint> 
        </client>
"@
    
    $clientNodes = $xmlDoc.SelectSingleNode("//system.serviceModel/client")
    # Check to see if the <client> node already exists.
    if ($clientNodes.Count -gt 0) {
        # The <client> node exists, so we'll replace it with the new one.
        # Set $clientNode to it's first child node.
        $clientNode = $clientNodes[0]
        
        # Replace the <client> node with $clientXML element
        $clientNode.ParentNode.ReplaceChild($xmlDoc.ImportNode($clientXml.DocumentElement, $true), $clientNode)
     } 
     else 
     {
        # The <client> node doesn't exist, so we'll create it.
        $serviceModelNode = $xmlDoc.SelectSingleNode('//system.serviceModel')
        
        # Load the $clientXml string into a new XmlDocument object.
        $newXmlDoc = New-Object System.Xml.XmlDocument
        $newXmlDoc.LoadXml($clientXml)

        # Get the $clientXml as a node
        $importedNode = $xmlDoc.ImportNode($newXmlDoc.DocumentElement, $true)
        
        # Append the $importedNode to the $serviceModelNode
        $serviceModelNode.AppendChild($importedNode)
        $xmlDoc.Save($configFilePath) 
     }

    return $xmlDoc
}
  
$webConfigPath = "C:\PATH TO WEB.CONFIG\Web.config"
AddClientToXml($webConfigPath)

After running the script the web.config specified to the function should resemble the following:

<system.serviceModel>
        <bindings>
            ...
        </bindings>
        <client> 
          <endpoint address="RegistrationAddressValue"
                behaviorConfiguration="SampleClientCertificateBehavior" binding="wsHttpBinding"
                bindingConfiguration="WSHttpBinding_RegistrationService"
                contract="Registration.RegistrationService" name="WSHttpBinding_RegistrationService">
                <identity>
                    <dns value="RegistrationDNS" />
                </identity>
            </endpoint> 
        </client>
      <behaviors>
        ...
      </behaviors>
    </system.serviceModel>

This approach is particularly useful when using CI/CD to deploy your code base to environments that require different configurations.

Hope this helps!

Using Roslyn to get a list of Implemented Interfaces for CSharpSyntaxRewriter

Standard

While working on a project to update method signature’s and add a parameter down the entire stack I ran into an issue when the method being altered is implemented as part of an interface, as the implementing class no longer matches the interfaces signature. Because of this I needed to get a collection of the implemented interfaces for the class beforehand which was not straightforward.

The information needed is available from INamedTypedSymbol for the class. This is obtained through the SemanticModel using the GetDeclaredSymbol method. Once you have this Symbol the interfaces are available through the AllInterfaces property.

    public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
    {
        var tree = node.SyntaxTree;
        var root = tree.GetRoot();   
        // Get the Semantic Model         
        var sModel = comp.GetSemanticModel(node.SyntaxTree);
        // Get symbol for the class
        var classSymbol = sModel.GetDeclaredSymbol(root.DescendantNodes().OfType<ClassDeclarationSyntax>().First());
        // Get classes implemented interfaces.
        var implementedInterfaces = classSymbol.AllInterfaces;

        return base.VisitClassDeclaration(node);
    }

IEditableObject and Restoring Text After Cancelling Changes

Standard

It’s a common scenario, the user starts to edit something and then changes their mind. What happens in an application with Two-Way binding? As soon as the user began to type you began persisting the data, in effect there is no going back.

Cue IEditableObject to the rescue!

The answer is to implement the IEditableObject interface on your class. The interface defines 3 void methods: BeginEdit(), CancelEdit(), & EndEdit().

In the BeginEdit() we set the editing mode to true and make a copy of the original value. If you were dealing with more than one field, I recommend getting a copy of your object using MemberwiseClone, unless your class includes reference types in which case you will want to make a deep copy instead.

public void BeginEdit()
{
     IsEditing = true;
     // Save a copy of the original value
     originalmessage = this.Messages;
}

public void CancelEdit()
{
      // Put the original value back
      this.Messages = originalmessage;
      IsEditing = false;
}

public void EndEdit()
{
      IsEditing = false;
      // Save the current value of the field
      repository.SaveMessages(this.Messages); 
}