Thursday, August 13, 2020

Performance: Linking a List of Child Objects To a List of Parent Objects

Many a time I've had to build a relationship where a parent object has a list of child objects linked to it, where the parent itself also belongs to a list. There are many ways of doing this, however, the most performant way I've found is to use an extension on the Enumerable class; ToLookup. Lets say we have a list of parent objects and a list of child objects, and that we need to link to each parent all its children. Parent Object:
public class Parent
{
    public long Identifier { get; set; }

    public List Children { get; set; } = new List();

    public Parent(long identifier)
    {
        this.Identifier = identifier;
    }
}
Child Object:
public class Child
{
    public long Identifier { get; set; }

    public long ParentIdentifier { get; set; }

    public Child(long identifier, long parentIdentifier)
    {
        this.Identifier = identifier;
        this.ParentIdentifier = parentIdentifier;
    }
}
We will assume we have some code that gets us a large list of parents and a large list of children to link to the parents. Each parent has an average of 25 children, some could have no children, some could have more than 25.
List parents = GetParents().ToList(); // 15,000 parents
List children = GetChildren().ToList(); // 300,000 children.
Typically, I would loop through the parents and do a search of the children for any that have a corresponding parent id.
foreach (var parent in parents)
{
    parent.Children = children.Where(d => d.ParentIdentifier == parent.Identifier).ToList();
}
While this method of linking the children to the parents is straightforward, and works fine for small data sets, it really hits the wall on a large dataset; such as our example. The above method takes an average of 88 seconds to complete the linking. We can do better. Lets use the Enumerable.ToLookup instead. We will use ToLookup to build a dictionary of the children grouped by the parent. Then its a simple retrieval of the children for a given parent.
var childrenLookup = children.ToLookup(d => d.ParentIdentifier);

foreach (var parent in parents)
{
    parent.Children = childrenLookup[parent.Identifier].ToList();
}
Using the ToLookup knocks our average linking time down to 0.08 seconds! That's a 99% performance increase!

SQL Server Profiler - Just My Queries

To record only the actions you are performing, and not everything else from all the other users and services, simply add a filter for the ClientProcessID.

Trace File Properties:
 - Column Filters
     - ClientProcessID
         - Set the value to the your clients process.

Monday, March 2, 2020

Read WCF Service Web.config Sections and Values

Get the Web.Config contents as a Configuration object.
private static Configuration GetWebConfig()
{
    var vdm = new VirtualDirectoryMapping(System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath, true);
    var wcfm = new WebConfigurationFileMap();
    wcfm.VirtualDirectories.Add("/", vdm);

    var webConfig = WebConfigurationManager.OpenMappedWebConfiguration(wcfm, "/");

    return webConfig;
}
Get a value from the appSettings section.
public static string GetConfigurationValue(string keyName)
{
    string value = null;

    var webConfig = GetWebConfig();

    if (webConfig.AppSettings.Settings.Count > 0)
    {
        var setting = webConfig.AppSettings.Settings[keyName];
        if (setting != null)
        {
            value = setting.Value;
        }
    }

    return value;
}
Get a specific binding.
public static Binding ResolveBinding(string name)
{
    var section = GetBindingsSection();
    var nameLower = name.ToLower();

    foreach (var bindingCollection in section.BindingCollections.Where(collection => collection.ConfiguredBindings.Any()))
    {
        var bindingElement = bindingCollection.ConfiguredBindings.FirstOrDefault(element => element.Name.ToLower() == nameLower);
        if (bindingElement == null)
        {
            continue;
        }

        var binding = (Binding)Activator.CreateInstance(bindingCollection.BindingType);
        binding.Name = bindingElement.Name;
        bindingElement.ApplyConfiguration(binding);

        return binding;
    }

    var message = $"Binding with name '{name}' could not be found in the service web.config.";
    throw new FaultException(new FaultReason(message));
}
Get a specific endpoint.
public static ChannelEndpointElement ResolveClientEndpoint(string name)
{
    var section = GetClientSection();
    var nameLower = name.ToLower();

    var endpoint = section.Endpoints.OfType()
        .FirstOrDefault(e => e.Name.ToLower() == nameLower);

    if (endpoint != null)
    {
        return endpoint;
    }

    var message = $"Client endpoint with name '{name}' could not be found in the service web.config.";
    throw new FaultException(new FaultReason(message));
}
Get the Binding section.
public static BindingsSection GetBindingsSection()
{
    var webConfig = GetWebConfig();
    var bindingSection = ServiceModelSectionGroup.GetSectionGroup(webConfig)?.Bindings;

    if (bindingSection != null)
    {
        return bindingSection;
    }

    const string message = "The service web.config is missing the bindings section.";
    throw new FaultException(new FaultReason(message));
}
Get the Client section.
public static ClientSection GetClientSection()
{
    var webConfig = GetWebConfig();
    var clientSection = ServiceModelSectionGroup.GetSectionGroup(webConfig)?.Client;

    if (clientSection != null)
    {
        return clientSection;
    }

    const string message = "The service web.config is missing the client section.";
    throw new FaultException(new FaultReason(message));
}

Thursday, September 19, 2019

ASP.Net Core – Overriding the Web.config

In ASP.Net Core, when publishing the project, the web.config file is generated with some default data.

Example:
<configuration>
  <system.webServer>
    <handlers>
      <add modules="AspNetCoreModule" name="aspNetCore" path="*" resourceType="Unspecified" verb="*" />
    </handlers>
    <aspNetCore processPath=".\My.WebApp.exe" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
  </system.webServer>
</configuration>
At times, you may need to override the contents of this file or change the values of some of the keys. To do this, simply add a web.config file to your ASP.Net Core project. You can add the file through the usual way of right-clicking the project and choosing "Add -> New Item…". Next, browse through the list of available templates and select Web Configuration File. This will add a new web.config file to your project with a commented out template which you can modify to fit your needs.

Example:
<configuration>

  <!-- To customize the asp.net core module uncomment and edit the following section. 
  For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->
  <!--
  <system.webServer>
    <handlers>
      <remove name="aspNetCore"/>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
  </system.webServer>
  -->

</configuration>
Simply uncomment the section and change, or add, any values you need. In the following example, I want to remove the arguments attribute as my application does not require it, and override the value of the processPath; my application exe is located in a sub folder named “bin”.

Example:
<configuration>
  <system.webServer>
    <handlers>
      <add modules="AspNetCoreModule" name="aspNetCore" path="*" resourceType="Unspecified" verb="*" />
    </handlers>
    <aspNetCore processPath=".\bin\My.WebApp.exe" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
  </system.webServer>
</configuration>
By adding the web.config file, most of the values in the web.config will be overridden when the project is published. However, for some reason, the customized processPath value gets ignored and the automatic generated value gets outputted instead. To fix this issue, you will need to modify your project file and add a new keyword to your Publish build profiles; IsTransformWebConfigDisabled. This will ensure that whatever you put in your web.config remains.

Example:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
  <PlatformTarget>x86</PlatformTarget>
  <DocumentationFile>.\bin\Release\net472\My.WebApp.xml</DocumentationFile>
  <NoWarn>1701;1702;1705;1591;NU1603</NoWarn>
  <Optimize>false</Optimize>
  <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>

Friday, June 28, 2019

WinForms: How To Add a Border To a Panel Control

To add a border to a Panel control, extend the control with the following class:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
 
class MyPanel : Panel
{
    private Color colorBorder = Color.Transparent;
 
    public MyPanel() : base()
    {
        this.SetStyle(ControlStyles.UserPaint, true);
    }
 
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        e.Graphics.DrawRectangle(
            new Pen(
                new SolidBrush(colorBorder), 2), e.ClipRectangle);
    }
 
    public Color BorderColor
    {
        get { return colorBorder; }
        set { colorBorder = value; }
    }
}
You can then use the new BorderColor property to set the border to any color you like.

Thursday, June 27, 2019

Useful Visual Studio Extensions and Addons

JetBrains ReSharper
ReSharper extends Visual Studio with over 1700 code inspections for C#, VB.NET, ASP.NET, JavaScript, TypeScript and other technologies. For most inspections, ReSharper provides quick-fixes (light bulbs) to improve code in one way or another.

SubMain GhostDoc
GhostDoc is a free Visual Studio extension that automatically generates XML documentation comments for methods and properties based on their type, parameters, name, and other contextual information.

HideShow Comments (Visual Studio 2010, 2012,2013)
Hides comments and replaces them with a callout icon. The comments are shown as tooltip and when editing them.

Visual Studio Spell Checker (Visual Studio 2013, 2015)
An editor extension that checks the spelling of comments, strings, and plain text as you type or interactively with a tool window. Extra options are available to control how elements and attributes in XML and MAML files are spell checked.

Ref12 (Visual Studio 2013, 2015, 2017, 2019)
This extension lets you press F12 or use Go To Definition command to open the source code for anything in the .Net framework. You can easily jump to the actual source for things linq LINQ or WinForms or ASP.Net and see how they're implemented, including original comments. Just place the cursor on any class, function, or constant in the .Net framework and press F12 to open your default browser. No more “From Metadata” tabs!

SpecFlow
Use SpecFlow to define, manage and execute automated acceptance tests from business-readable specifications. SpecFlow acceptance tests follow the BDD paradigm: define specifications using examples understandable to business users as well as developers and testers. SpecFlow integrates with Visual Studio, but can be also used from the command line (e.g. on a build server).

Visual Studio Favorites (Visual Studio 2015, 2017, 2019)
Add a Favorites menu to Visual Studio to keep track of locations in Source Control or files in a Solution.

Wednesday, June 26, 2019

Tuesday, June 25, 2019

WPF: How To Remove The Min, Max and Close Buttons On a WPF Window

WPF doesn’t have a built-in property to hide the title bar’s Min, Max and Close buttons, however, you can do it with a few lines of P/Invoke.

First, add these declarations to your Window class:
private const int GWL_STYLE = -16;
private const int WS_SYSMENU = 0x80000;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

Then put this code in the Window’s Loaded Event:
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
    var hwnd = new WindowInteropHelper(this).Handle;
    SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}

No more window buttons. You also won’t have a window icon on the left side of the title bar, which means no System menu, even when you right-click the title bar – they all go together.

Note that Alt+F4 will still close the Window. If you don’t want to allow the window to close before the background thread is done, then you could also override OnClosing and set Cancel to true.

Saturday, June 22, 2019

Simple Code For (Un)Installing An Assembly To The GAC

The Microsoft tool GacUtil.exe could be in one of many places depending on what version of .Net is installed.
Rather than have a user try to find it, we can leverage the .Net Framework directly to perform GAC (un)install.
This is ideal for use in a Console application to (un)install a specific file.

Microsoft Links
Publish.GacInstall Method
Publish.GacRemove Method

Example:
Simple installer class that is installing the MyAssembly.dll assembly into the GAC.
ToDo: This may be more useful if it accepts the file name as a parameter.

using System;
using System.IO;
using System.EnterpriseServices.Internal; 
 
class GACInstaller
{
    static void Main(string[] args)
    {
        string assemblyName = "MyAssembly.dll";
        Console.WriteLine();
        Publish objPublish = new Publish();
        string file = Path.Combine(Directory.GetCurrentDirectory(), assemblyName);
 
        if (File.Exists(file))
        {
            try
            {
                objPublish.GacInstall(file);
                Console.WriteLine($"'{assemblyName}' successfully added to the cache");
            }
            catch (Exception error)
            {
                Console.WriteLine($"Error encountered when adding '{assemblyName}' to cache: {error.Message}");
            }
        }
        else
        {
            Console.WriteLine($"Cannot find specified '{assemblyName}' in current directory");
        }
    }
}

Wednesday, June 5, 2019

C#: Convert List<long> to List<string> and Vice Versa

List listOfNumbers = new List() { 1, 2, 3 };
List listOfStrings = listOfNumbers.ConvertAll(n => n.ToString());
listOfNumbers = listOfStrings.ConvertAll(long.Parse);

Tuesday, June 4, 2019

SQL: Concatenate Fields From Multiple Rows In SQL

In SQL Server 2005 and up you can use the following SQL code to concatenate fields from multiple rows.

Sample Data:
declare @YourTable table
(
    RowID int,
    HeaderValue int,
    ChildValue varchar(5)
)

insert into @YourTable values (1, 1, 'CCC')
insert into @YourTable values (2, 2, 'BBB')
insert into @YourTable values (3, 2, 'AAA')
insert into @YourTable values (4, 3, '123')
insert into @YourTable values (5, 3, 'A & Z')
RowID HeaderValue ChildValue
1     1           CCC
2     2           BBB
3     2           AAA
4     3           123
5     3           A & Z
The Concatenation Code:
set nocount off

select
    t1.HeaderValue,
stuff(
        (select ', ' -- delimiter
           + t2.ChildValue
         from @YourTable t2
         where t1.HeaderValue=t2.HeaderValue
         order by t2.ChildValue
         for xml path(''), TYPE).value('.','varchar(max)'),
       1, 2, '') as ChildValues
from @YourTable t1
group by t1.HeaderValue
Output:
HeaderValue ChildValues
----------- -------------------
1           CCC
2           AAA, BBB
3           123, A & Z

Performance: Linking a List of Child Objects To a List of Parent Objects

Many a time I've had to build a relationship where a parent object has a list of child objects linked to it, where the parent itself als...