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";
    }
}

Creating an In Memory Zip Archive for multiple files in C#

Standard

I was asked an interesting technical question the other day that felt relevant enough to share. Having tackled a similar issue in the distant past using SharpLib this felt like a good opportunity to recreate the functionality using native .Net functionality in the form of ZipArchive!

For this use case the ask was to store various string data that can be appended to an in memory zip archive in form of a new file containing the data. After the data is processed the desire was to write the zip file to a specific location for another process. The following is short proof of concept to outline the implementation.

using System.IO.Compression;
using System.Text;

namespace ZipTest
{
    internal class Program
    {
        /// <summary>
        /// Create a new file to contain the data passed in and then add that to the Zip Archive
        /// </summary>
        /// <param name="archive">The Archive to append to</param>
        /// <param name="fileName">The name of the file as you wish it to appear in the archive</param>
        /// <param name="textContent"></param>
        static void AddFileToZip(ZipArchive archive, string fileName, string textContent)
        {
            // Create a new entry in the archive using the specified file name
            ZipArchiveEntry entry = archive.CreateEntry(fileName);

            // Write the text content to the entry
            using (Stream entryStream = entry.Open())
            using (StreamWriter writer = new StreamWriter(entryStream, Encoding.UTF8))
            {
                writer.Write(textContent);
            }
        }
        // Append the data in the form of a file to the archive
        static void AppendFileToZipArchive(MemoryStream zipStream, string fileName, string textContent)
        {
            // Create a new ZipArchive instance using the existing MemoryStream
            using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Update, leaveOpen: true))
            {
                AddFileToZip(archive, fileName, textContent);
            }
        }

        static void SaveZipArchiveToDisk(MemoryStream zipArchiveStream, string outputPath)
        {
            // Save the MemoryStream to a file
            File.WriteAllBytes(outputPath, zipArchiveStream.ToArray());
        }
        static void Main(string[] args)
        {
            using (MemoryStream zipArchiveStream = new MemoryStream())
            {
                // Append files to the zip archive
                AppendFileToZipArchive(zipArchiveStream, "file1.txt", "This is the content of file 1.");
                AppendFileToZipArchive(zipArchiveStream, "file2.txt", "This is the content of file 2.");

                // Save the zip archive to a file
                SaveZipArchiveToDisk(zipArchiveStream, "result.zip");
            }
        }
    }
}

Hopefully you find this useful!

LINQ and problem of Premature Materialization

Standard

I am a huge believer in the power and usefulness of LINQ. At the same time I often see common mistakes that can have large impacts on performance. When working with an IEnumerable it’s important to remember that you may not be working with the actual data, but a promise of data being available.

IEnumerable represents the concept of a collection of data. Similar to a cursor in SQL, an IEnumerable is forward only traversable collection that loads and iterates over the records one at a time. If you have a 1000 records read from a database using an IEnumerable, there is technically only one record in memory at a given time.

Materialization in Memory
When you force materialization in memory, all records must be loaded into memory for manipulation. I’m going to use the following method as an example.

    string GetDefaultSMSPhoneNumber(IEnumerable<PhoneNumbers> patientNumbers)
    {
        const int PHONE_TYPE_HOME = 1;
        const int PHONE_TYPE_OFFICE = 3;
        const int PHONE_TYPE_OTHER = 9;

        var phoneNumberByType = patientNumbers.Where(p => p.sms_capable == 1).GroupBy(p => p.phone_type_id);

        // Select the phone number last used in creating a prescription
        if (patientNumbers.Where(p => p.sms_capable == 1 && p.last_used_for_rx == 1).Count() > 0)
        {
            return patientNumbers.Where(p => p.sms_capable == 1 && p.last_used_for_rx == 1).FirstOrDefault().phone_number;
        }

        // If no number has been used, select a configured SMS number in the following order (Other, Home, Office) 
        if (patientNumbers.Where(p => p.sms_capable == 1 && p.phone_type_id == PHONE_TYPE_OTHER).Count() > 0)
        {
            return patientNumbers.Where(p => p.sms_capable == 1 && p.phone_type_id == PHONE_TYPE_OTHER).FirstOrDefault().phone_number;
        }

        // If no number has been used, select a configured SMS number in the following order (Other, Home, Office) 
        if (patientNumbers.Where(p => p.sms_capable == 1 && p.phone_type_id == PHONE_TYPE_HOME).Count() > 0)
        {
            return patientNumbers.Where(p => p.sms_capable == 1 && p.phone_type_id == PHONE_TYPE_HOME).FirstOrDefault().phone_number;
        }

        // If no number has been used, select a configured SMS number in the following order (Other, Home, Office) 
        if (patientNumbers.Where(p => p.sms_capable == 1 && p.phone_type_id == PHONE_TYPE_OFFICE).Count() > 0)
        {
            return patientNumbers.Where(p => p.sms_capable == 1 && p.phone_type_id == PHONE_TYPE_OFFICE).FirstOrDefault().phone_number;
        }

        return string.Empty;
    }

Aside from high cyclomatic complexity, this method materializes the collection a minimum of 3 times and a maximum of 9 times. Some of the issues above can remedied simply by removing the uneeded or redundant calls. For example:

// The following statement can be cleaned up a few different ways
if (patientNumbers.Where(p => p.sms_capable == 1 && p.last_used_for_rx == 1).Count() > 0)
{
     return patientNumbers.Where(p => p.sms_capable == 1 && p.last_used_for_rx == 1).FirstOrDefault().phone_number;
}

// The criteria in the Where can be moved to the count
// Because it's only looking for existence, count should be replaced with Any
// This approach will iterate the collection twice, once for the Any and once for FirstOrDefault
if (patientNumbers.Any(p => p.sms_capable == 1 && p.last_used_for_rx == 1))
{
     // Similarly he criteria in the Where clause can moved directly to the FirstOrDefault
     return patientNumbers.FirstOrDefault(p => p.sms_capable == 1 && p.last_used_for_rx == 1).phone_number;
}

// Alternatively you can do the following which will only materialize your collection once
var patient = patientNumbers.FirstOrDefault(p => p.sms_capable == 1 && p.last_used_for_rx == 1);
if (patient != null)
    return patient.phone_number
 

While refactoring our “if” statements cleans up our code, each one is still materializing our collection which is undesirable. If we look at the objective of our method we can refactor the logic to use Linq itself instead of using logic on top of data retrieved from Linq. We a list of phone numbers, broken into prioritized group and we want to return the first eligible number.

        string GetDefaultSMSPhoneNumber(IEnumerable<PhoneNumbers> patientNumbers)
        {
            // Because the numeric values for the phone types don't match the order of importance
            // a dictionary is used to order number by the desired priority.
            var phoneTypeSortOrder = new Dictionary<int, int> { { (int)PhoneType.Other, 1 }, { (int)PhoneType.Home, 2 }, { (int)PhoneType.Office, 3 } };
            
            // Filter to only SMS capable number and check to see if a number has been previously used
            var smsNumbers = patientNumbers.Where(p => p.sms_capable == 1);              
            var lastUsedNumber = smsNumbers.FirstOrDefault(p => p.last_used_for_rx == 1);
            if (lastUsedNumber != null) 
                return lastUsedNumber.phone_number; 
            
            // If no number has been used, select a configured SMS number in the following order (Other, Home, Office)             
            var configuredNumbers = smsNumbers.Where(x => phoneTypeSortOrder.ContainsKey(x.phone_type_id))
                .GroupBy(p => phoneTypeSortOrder[p.phone_type_id])  // Put the number into groups based on type
                .OrderBy(g => g.Key)                                // Order the numbers by their group
                .SelectMany(g => g)                                 // Apply the order to all the numbers in group
                .FirstOrDefault();                                  // Return the first phone number
            return configuredNumbers?.phone_number ?? string.Empty; 
        }

As you can see the refactored code is much smaller, is still easy to read, and is more performant in all scenarios. In the best case scenario the collection is materialized only once when the Phone Number last used for Rx is retrieved. In the worse case scenario our collection is materialized twice. Once as referenced above and once during the GroupBy operation.

Hopefully this provides some help and insight as you continue to work with Linq.
Happy to hear your thoughts!

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);
    }

WPF – Advanced Animation Effects

Standard

When designing an application there are many reasons to use an Animation Effect to an element in order to draw attention to it. My specific implementation was for monitoring application but the effects can be leveraged in many scenarios. Here are 5 common variations of requested animation effects.

GIF Example of Output

The XAML

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" x:Class="MainWindow"
    Title="MainWindow" Height="450" Width="250">
    <Window.Resources>

        <!-- Marching Ants -->
        <Storyboard x:Key="MarchingAnts">
                <DoubleAnimation BeginTime="00:00:00"
                                Storyboard.TargetName="AlertBox"
                                Storyboard.TargetProperty="StrokeThickness"
                                To="4"
                                Duration="0:0:0.25" />
                           <!-- If you want to run counter-clockwise, just swap the 'From' and 'To' values. -->
                <DoubleAnimation BeginTime="00:00:00" RepeatBehavior="Forever" Storyboard.TargetName="AlertBox" Storyboard.TargetProperty="StrokeDashOffset" 
                                Duration="0:3:0" From="1000" To="0"/>
        </Storyboard>

        <!-- Pulse -->
        <Storyboard x:Key="Pulse" RepeatBehavior="Forever">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="PulseBox">
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1.15"/>
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="PulseBox">
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1.15"/>
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>

        <!-- Flipper -->
        <Storyboard x:Key="Flipper" RepeatBehavior="Forever">
            <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="FlipperBox">
                <EasingPointKeyFrame KeyTime="0:0:1" Value="0.5,0.5"/>
                <EasingPointKeyFrame KeyTime="0:0:2" Value="0.5,0.5"/>
            </PointAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="FlipperBox">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="-1"/>
                <EasingDoubleKeyFrame KeyTime="0:0:2" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>


        <!-- Elasic Lines -->
        <Storyboard x:Key="ElasticLines" RepeatBehavior="Forever" AutoReverse="True">
            <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.Fill).(LinearGradientBrush.EndPoint)" Storyboard.TargetName="ElasticBox">
                <EasingPointKeyFrame KeyTime="0:0:4" Value="12,8"/>
            </PointAnimationUsingKeyFrames>
        </Storyboard>

        <!-- Knight Rider -->
        <Storyboard x:Key="KnightRider" RepeatBehavior="Forever" AutoReverse="True">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="KRBox">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="-50"/>
                <EasingDoubleKeyFrame KeyTime="0:0:2" Value="50"/>
                <EasingDoubleKeyFrame KeyTime="0:0:3" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>

    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard Storyboard="{StaticResource Pulse}"/>
            <BeginStoryboard Storyboard="{StaticResource MarchingAnts}"/>
            <BeginStoryboard Storyboard="{StaticResource Flipper}"/>
            <BeginStoryboard Storyboard="{StaticResource ElasticLines}"/>
            <BeginStoryboard Storyboard="{StaticResource KnightRider}"/>
        </EventTrigger>
    </Window.Triggers>

    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
        <Grid.Resources>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="Foreground" Value="White"/>
                <Setter Property="FontWeight" Value="Bold"/>
                <Setter Property="FontSize" Value="35"/>
                <Setter Property="HorizontalAlignment" Value="Center"/>
                <Setter Property="VerticalAlignment" Value="Center"/>
                <Setter Property="Text" Value="ALERT"/>
            </Style>
            <Style TargetType="{x:Type Grid}">
                <Setter Property="Margin" Value="0,10"/>                
            </Style>
            <Style TargetType="{x:Type Rectangle}">
                <Setter Property="Height" Value="50"/>
                <Setter Property="Width" Value="150"/>
            </Style>
        </Grid.Resources>

        <StackPanel>

        <!-- Marching Ants -->
        <Grid>

            <Rectangle x:Name="AlertBox"
                      Stroke="Red" 
                       StrokeDashOffset="2" StrokeDashArray="5" Margin="5">
                <Rectangle.Fill>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="6,4" MappingMode="Absolute" SpreadMethod="Repeat">
                        <GradientStop Color="Red" Offset="0.25"/>
                        <GradientStop Color="#00000000" Offset="0.15"/>
                    </LinearGradientBrush>
                </Rectangle.Fill>
            </Rectangle>

            <TextBlock/>

        </Grid>
        <!-- End Marching Ants -->


        <!-- Pulse : Will not skew other elements location like width/height animations would. -->
        <Grid>
            <Border x:Name="PulseBox"
                        Background="Red" RenderTransformOrigin="0.5,0.5">
                <Border.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform/>
                        <TranslateTransform/>
                    </TransformGroup>
                </Border.RenderTransform>

                <TextBlock/>

            </Border>
        </Grid>
        <!-- End Pulse -->


        <!-- Flipper -->
        <Grid>
            <Border x:Name="FlipperBox"
                        Background="Red">
                <Border.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform/>
                        <TranslateTransform/>
                    </TransformGroup>
                </Border.RenderTransform>

                <TextBlock/>

            </Border>
        </Grid>
        <!-- End Flipper -->


        <!-- Elastic Lines -->
        <Grid>
            <Rectangle x:Name="ElasticBox"
                      Stroke="Red" StrokeThickness="5" Margin="5">
                <Rectangle.Fill>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="6,4" MappingMode="Absolute" SpreadMethod="Repeat">
                        <GradientStop Color="Red" Offset="0.25"/>
                        <GradientStop Color="#00000000" Offset="0.15"/>
                    </LinearGradientBrush>
                </Rectangle.Fill>
            </Rectangle>

            <TextBlock/>

        </Grid>
        <!-- End Elastic Box -->


        <!-- Knight Rider -->
        <Grid>
            <Rectangle Fill="Red"/>
            <Rectangle x:Name="KRBox" Width="50" Fill="White" RenderTransformOrigin="0.5,0.5">
                <Rectangle.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform/>
                        <TranslateTransform/>
                    </TransformGroup>
                </Rectangle.RenderTransform>
            </Rectangle>

            <TextBlock Foreground="Red"/>

        </Grid>
        <!-- End Knight Rider -->

        </StackPanel>

    </Grid>
</Window>

Hopefully this helps you with your animation needs!

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); 
}