<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://shazwazza.com/rss/xslt"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>Shazwazza</title>
    <link>https://shazwazza.com/</link>
    <description>My blog which is pretty much just all about coding</description>
    <generator>Articulate, blogging built on Umbraco</generator>
    <image>
      <url>/media/0libq25y/frog.png?rmode=max&amp;v=1da0e911f4e6890</url>
      <title>Shazwazza</title>
      <link>https://shazwazza.com/</link>
    </image>
    <item>
      <guid isPermaLink="false">1200</guid>
      <link>https://shazwazza.com/post/how-to-execute-one-controller-action-from-another-in-aspnet-5/</link>
      <category>ASP.Net</category>
      <category>Umbraco</category>
      <title>How to execute one Controller Action from another in ASP.NET 5</title>
      <description>&lt;p&gt;This will generally be a rare thing to do but if you have your reasons to do it, then this is how…&lt;/p&gt;
&lt;p&gt;In Umbraco one valid reason to do this is due to how HTTP POSTs are handled for forms. Traditionally an HTML form will POST to a specific endpoint, that endpoint will handle the validation (etc), and if all is successful it will redirect to another URL, else it will return a validation result on the current URL (i.e. PRG POST/REDIRECT/GET). In the CMS world this may end up a little bit weird because URLs are dynamic. POSTs in theory should just POST to the current URL so that if there is a validation result, this is still shown on the current URL and not a custom controller endpoint URL. This means that there can be multiple controllers handling the same URL, one for GET, another for POST and that’s exactly what Umbraco has been doing since MVC was enabled in it many years ago. For this to work, a controller is selected during the dynamic route to handle the POST (a &lt;em&gt;SurfaceController&lt;/em&gt; in Umbraco) and if successful, typically the developer will use: &lt;em&gt;return&lt;/em&gt; &lt;em&gt;RedirectToCurrentUmbracoPage&lt;/em&gt; (of type &lt;em&gt;RedirectToUmbracoPageResult&lt;/em&gt;) or if not successful will use: &lt;em&gt;return&lt;/em&gt; &lt;em&gt;CurrentUmbracoPage&lt;/em&gt; (of type &lt;em&gt;UmbracoPageResult&lt;/em&gt;). The &lt;em&gt;RedirectToUmbracoPageResult&lt;/em&gt; is easy to handle since this is just a redirect but the &lt;em&gt;UmbracoPageResult&lt;/em&gt; is a little tricky because one controller has just handled the POST request but now it wants to return a page result for the current Umbraco page which is handled by a different controller.&lt;/p&gt;
&lt;h2&gt;IActionInvoker&lt;/h2&gt;
&lt;p&gt;The concept is actually pretty simple and the &lt;em&gt;IActionInvoker&lt;/em&gt; does all of the work. You can create an &lt;em&gt;IActionInvoker&lt;/em&gt; from the &lt;em&gt;IActionInvokerFactory&lt;/em&gt; which needs an &lt;em&gt;ActionContext&lt;/em&gt;. Here’s what the &lt;em&gt;ExecuteResultAsync&lt;/em&gt; method of a custom &lt;em&gt;IActionResult&lt;/em&gt; could look like to do this:&lt;/p&gt;
&lt;pre&gt;public async Task ExecuteResultAsync(ActionContext context)
{
    // Change the route values to match the action to be executed
    context.RouteData.Values["controller"] = "Page";
    context.RouteData.Values["action"] = "Index";

    // Create a new context and excute the controller/action
    // Copy the action context - this also copies the ModelState
    var renderActionContext = new ActionContext(context)
    {
        // Normally this would be looked up via the EndpointDataSource
        // or using the IActionSelector
        ActionDescriptor = new ControllerActionDescriptor
        {
            ActionName = "Index",
            ControllerName = "Page",
            ControllerTypeInfo = typeof(PageController).GetTypeInfo(),
            DisplayName = "PageController.Index"
        }
    };

    // Get the factory
    IActionInvokerFactory actionInvokerFactory = context.HttpContext
                .RequestServices
                .GetRequiredService&amp;lt;IActionInvokerFactory&amp;gt;();

    // Create the invoker
    IActionInvoker actionInvoker = actionInvokerFactory.CreateInvoker(renderActionContext);

    // Execute!
    await actionInvoker.InvokeAsync();
}&lt;/pre&gt;
&lt;p&gt;That’s pretty must the gist of it. The note about the &lt;em&gt;ControllerActionDescriptor&lt;/em&gt; is important though, it’s probably best to not manually create these since they are already created with all of your routing. They can be queried and resolved in a few different ways such as interrogating the &lt;em&gt;EndpointDataSource&lt;/em&gt; or using the &lt;em&gt;IActionSelector&lt;/em&gt;. This execution will execute the entire pipeline for the other controller including all of it’s filters, etc…&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:10:18 Z</pubDate>
      <a10:updated>2023-03-23T15:10:18Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1279</guid>
      <link>https://shazwazza.com/post/allowing-dynamic-supportedcultures-in-requestlocalizationoptions/</link>
      <category>ASP.Net</category>
      <title>Allowing dynamic SupportedCultures in RequestLocalizationOptions</title>
      <description>&lt;p&gt;The documented usage of &lt;a rel="noopener" href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.requestlocalizationoptions?view=aspnetcore-5.0" target="_blank"&gt;&lt;em&gt;RequestLocalizationOptions&lt;/em&gt;&lt;/a&gt; in ASP.NET 5/Core is to assign a static list of &lt;a rel="noopener" href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.requestlocalizationoptions.supportedcultures?view=aspnetcore-5.0#Microsoft_AspNetCore_Builder_RequestLocalizationOptions_SupportedCultures" target="_blank"&gt;&lt;em&gt;SupportedCultures&lt;/em&gt;&lt;/a&gt; since ASP.NET is assuming you’ll know up-front what cultures your app is supporting. But what if you are creating a CMS or another web app that allows users to include cultures dynamically?&lt;/p&gt;
&lt;p&gt;This isn’t documented anywhere but it’s certainly possible. &lt;a rel="noopener" href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.requestlocalizationoptions.supportedcultures?view=aspnetcore-5.0#Microsoft_AspNetCore_Builder_RequestLocalizationOptions_SupportedCultures" target="_blank"&gt;&lt;em&gt;RequestLocalizationOptions.SupportedCultures&lt;/em&gt;&lt;/a&gt; is a mutable &lt;em&gt;IList&lt;/em&gt; which means that values can be added/removed at runtime if you really want.&lt;/p&gt;
&lt;h2&gt;Create a custom RequestCultureProvider&lt;/h2&gt;
&lt;p&gt;First thing you need is a custom &lt;em&gt;RequestCultureProvider&lt;/em&gt;. The trick is to pass in the &lt;em&gt;RequestLocalizationOptions&lt;/em&gt; into it’s ctor so you can dynamically modify the &lt;em&gt;SupportedCultures&lt;/em&gt; when required.&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;public class MyCultureProvider : RequestCultureProvider
{
    private readonly RequestLocalizationOptions _localizationOptions;
    private readonly object _locker = new object();

    // ctor with reference to the RequestLocalizationOptions
    public MyCultureProvider(RequestLocalizationOptions localizationOptions)
        =&amp;gt; _localizationOptions = localizationOptions;

    public override Task&amp;lt;ProviderCultureResult&amp;gt; DetermineProviderCultureResult(HttpContext httpContext)
    {
        // TODO: Implement GetCulture() to get a culture for the current request
        CultureInfo culture = GetCulture(); 

        if (culture is null)
        {
            return NullProviderCultureResult;
        }

        lock (_locker)
        {
            // check if this culture is already supported
            var cultureExists = _localizationOptions.SupportedCultures.Contains(culture);

            if (!cultureExists)
            {
                // If not, add this as a supporting culture
                _localizationOptions.SupportedCultures.Add(culture);
                _localizationOptions.SupportedUICultures.Add(culture);
            } 
        }

        return Task.FromResult(new ProviderCultureResult(culture.Name));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Add your custom culture provider&lt;/h2&gt;
&lt;p&gt;You can configure &lt;em&gt;RequestLocalizationOptions&lt;/em&gt; in a few different ways, this example registers a custom implementation of &lt;em&gt;IConfigureOptions&amp;lt;RequestLocalizationOptions&amp;gt;&lt;/em&gt; into DI&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;public class MyRequestLocalizationOptions : IConfigureOptions&amp;lt;RequestLocalizationOptions&amp;gt;
{
    public void Configure(RequestLocalizationOptions options)
    {
        // TODO: Configure other options parameters

        // Add the custom provider,
        // in many cases you'll want this to execute before the defaults
        options.RequestCultureProviders.Insert(0, new MyCultureProvider(options));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then just register these options: &lt;em&gt;Services.ConfigureOptions&amp;lt;MyRequestLocalizationOptions&amp;gt;();&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That’s it, now you can have dynamic &lt;em&gt;SupportedCultures&lt;/em&gt; in your app!&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:10:14 Z</pubDate>
      <a10:updated>2023-03-23T15:10:14Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1309</guid>
      <link>https://shazwazza.com/post/controller-scoped-model-binding-in-aspnet-core/</link>
      <category>ASP.Net</category>
      <title>Controller Scoped Model Binding in ASP.NET Core</title>
      <description>&lt;p&gt;Want to avoid &lt;em&gt;[FromBody]&lt;/em&gt; attributes everywhere? Don’t want to use &lt;em&gt;[ApiController]&lt;/em&gt; strict conventions? Don’t want to apply &lt;em&gt;IInputFormatter&lt;/em&gt;’s globally?&lt;/p&gt;
&lt;p&gt;ASP.NET Core MVC is super flexible but it very much caters towards configuring everything at a global level. Perhaps you are building a framework or library or a CMS in .NET Core? In which case you generally want to be as unobtrusive as possible so mucking around with global MVC configuration isn’t really acceptable. The traditional way of dealing with this is by applying configuration directly to controllers which generally means using controller base classes and attributes. This isn’t super pretty but it works in almost all cases from applying authorization/resource/action/exception/result filters to api conventions. However this doesn’t work for model binding.&lt;/p&gt;
&lt;h2&gt;Model binding vs formatters&lt;/h2&gt;
&lt;p&gt;Model binding comes in 2 flavors: formatters for deserializing the request body (like JSON) into models  and value providers for getting data from other places like form body, query string, headers, etc… Both of these things internally in MVC use model binders though typically the language used for binding the request body are called formatters. The problem with formatters (which are of type &lt;a rel="noopener" href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.formatters.iinputformatter?view=aspnetcore-3.1" target="_blank"&gt;IInputFormatter&lt;/a&gt;) is that they are only applied at the global level as part of &lt;a rel="noopener" href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.mvcoptions.inputformatters?view=aspnetcore-3.1#Microsoft_AspNetCore_Mvc_MvcOptions_InputFormatters" target="_blank"&gt;MvcOptions&lt;/a&gt; which are in turn passed along to a special model binder called &lt;a rel="noopener" href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.modelbinding.binders.bodymodelbinder?view=aspnetcore-3.1" target="_blank"&gt;BodyModelBinder&lt;/a&gt;. Working with &lt;em&gt;IInputFormatter&lt;/em&gt; at the controller level is almost impossible.&lt;/p&gt;
&lt;p&gt;There seems to be a couple options that look like you might be able to apply a custom &lt;em&gt;IInputFormatter&lt;/em&gt; to a specific controller:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a custom &lt;em&gt;IModelBinderProvider&lt;/em&gt; – this unfortunately will not work because the &lt;em&gt;ModelBinderProviderContext&lt;/em&gt; doesn’t provide the &lt;em&gt;ControllerActionDescriptor&lt;/em&gt; executing so you cannot apply this provider to certain controllers/actions (&lt;a rel="noopener" href="https://github.com/dotnet/aspnetcore/issues/21724" target="_blank"&gt;though this should be possible&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Assign a custom &lt;em&gt;IModelBinderFactory&lt;/em&gt; to the controller explicitly by assigning &lt;em&gt;ControllerBase.ModelBinderFactory &lt;/em&gt;in the controllers constructor – this unfortunately doesn’t work because the &lt;em&gt;ControllerBase.ModelBinderFactory &lt;/em&gt;&lt;a rel="noopener" href="https://github.com/dotnet/aspnetcore/issues/21724" target="_blank"&gt;isn’t used for body model binding&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;So how does [ApiController] attribute work?&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;[ApiController]&lt;/em&gt; attribute does quite a lot of things and &lt;a rel="noopener" href="https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-3.1#apicontroller-attribute" target="_blank"&gt;configures your controller in a very opinionated way&lt;/a&gt;. It almost does what I want and it somehow magically does this&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[FromBody] is inferred for complex type parameters&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That’s great! It’s what I want to do but I don’t want to use the &lt;em&gt;[ApiController]&lt;/em&gt; attribute since it applies too many conventions and the only way to toggle these …. is again at the global level :/ This also still doesn’t solve the problem of applying a specific &lt;em&gt;IInputFormatter&lt;/em&gt; to be used for the model binding but it’s a step in the right direction.&lt;/p&gt;
&lt;p&gt;The way that the &lt;em&gt;[ApiController]&lt;/em&gt; attribute works is by using MVC’s &lt;a rel="noopener" href="https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/application-model?view=aspnetcore-3.1" target="_blank"&gt;“application model”&lt;/a&gt; which is done by implementing &lt;a rel="noopener" href="https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/application-model?view=aspnetcore-3.1#iapplicationmodelprovider" target="_blank"&gt;IApplicationModelProvider&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;A custom IApplicationModelProvider&lt;/h2&gt;
&lt;p&gt;Taking some inspiration from the way [ApiController] attribute works we can have a look at the source of the application model that makes this happen: &lt;a rel="noopener" href="https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ApplicationModels/ApiBehaviorApplicationModelProvider.cs" target="_blank"&gt;ApiBehaviorApplicationModelProvider&lt;/a&gt;. This basically assigns a bunch of &lt;a rel="noopener" href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.applicationmodels.iactionmodelconvention?view=aspnetcore-3.1" target="_blank"&gt;IActionModelConvention&lt;/a&gt;’s: &lt;em&gt;ApiVisibilityConvention&lt;/em&gt;, &lt;em&gt;ClientErrorResultFilterConvention&lt;/em&gt;, &lt;em&gt;InvalidModelStateFilterConvention&lt;/em&gt;, &lt;em&gt;ConsumesConstraintForFormFileParameterConvention&lt;/em&gt;, &lt;em&gt;ApiConventionApplicationModelConvention&lt;/em&gt;, and &lt;em&gt;InferParameterBindingInfoConvention&lt;/em&gt;. The last one &lt;strong&gt;InferParameterBindingInfoConvention &lt;/strong&gt;is the important one that magically makes complex type parameters bind from the request body like JSON like good old WebApi used to do.&lt;/p&gt;
&lt;p&gt;So we can make our own application model to target our own controllers and use a custom &lt;em&gt;IActionModelConvention&lt;/em&gt; to apply a custom body model binder:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;
public class MyApplicationModelProvider : IApplicationModelProvider
{
    public MyApplicationModelProvider(IModelMetadataProvider modelMetadataProvider)
    {
        ActionModelConventions = new List&amp;lt;IActionModelConvention&amp;gt;()
        {
            // Ensure complex models are bound from request body
            new InferParameterBindingInfoConvention(modelMetadataProvider),
            // Apply custom IInputFormatter to the request body
            new MyModelBinderConvention()
        };
    }

    public List&amp;lt;IActionModelConvention&amp;gt; ActionModelConventions { get; }

    public int Order =&amp;gt; 0;

    public void OnProvidersExecuted(ApplicationModelProviderContext context)
    {
    }

    public void OnProvidersExecuting(ApplicationModelProviderContext context)
    {
        foreach (var controller in context.Result.Controllers)
        {
            // apply conventions to all actions if attributed with [MyController]
            if (IsMyController(controller))
                foreach (var action in controller.Actions)
                    foreach (var convention in ActionModelConventions)
                        convention.Apply(action);
        }
    }

    // returns true if the controller is attributed with [MyController]
    private bool IsMyController(ControllerModel controller)
        =&amp;gt; controller.Attributes.OfType&amp;lt;MyControllerAttribute&amp;gt;().Any();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the custom convention:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;
public class MyModelBinderConvention : IActionModelConvention
{
    public void Apply(ActionModel action)
    {
        foreach (var p in action.Parameters
            // the InferParameterBindingInfoConvention must execute first,
            // which assigns this BindingSource, so if that is assigned
            // we can then assign a custom BinderType to be used.
            .Where(p =&amp;gt; p.BindingInfo?.BindingSource == BindingSource.Body))
        {
            p.BindingInfo.BinderType = typeof(MyModelBinder);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Based on the above application model conventions, any controller attributed with our custom &lt;em&gt;[MyController]&lt;/em&gt; attribute will have these conventions applied to all of it’s actions. With the above, any complex model that will be bound from the request body will use the &lt;em&gt;IModelBinder&lt;/em&gt; type: &lt;em&gt;MyModelBinder, &lt;/em&gt;so here’s how that implementation could look:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;
// inherit from BodyModelBinder - it does a bunch of magic like caching
// that we don't want to miss out on
public class MyModelBinder : BodyModelBinder
{
    // TODO: You can inject other dependencies to pass to GetInputFormatter
    public MyModelBinder(IHttpRequestStreamReaderFactory readerFactory)
        : base(GetInputFormatter(), readerFactory)
    {
    }

    private static IInputFormatter[] GetInputFormatter()
    {  
        return new IInputFormatter[]
        {
            // TODO: Return any IInputFormatter you want
            new MyInputFormatter()
        };
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last thing to do is wire it up in DI:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;
services.TryAddSingleton&amp;lt;MyModelBinder&amp;gt;();            
services.TryAddEnumerable(
    ServiceDescriptor.Transient&amp;lt;IApplicationModelProvider,
    MyApplicationModelProvider&amp;gt;());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s a reasonable amount of plumbing!&lt;/p&gt;
&lt;p&gt;It could certainly be simpler to configure a body model binder at the controller level but at least there’s actually a way to do it. For a single controller this is quite a lot of work but for a lot of controllers the MVC “application mode” is quite brilliant! … it just took a lot of source code reading to figure that out :)&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:09:49 Z</pubDate>
      <a10:updated>2023-03-23T15:09:49Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1222</guid>
      <link>https://shazwazza.com/post/how-to-register-mvc-controllers-shipped-with-a-class-library-in-aspnet-core/</link>
      <category>ASP.Net</category>
      <title>How to register MVC controllers shipped with a class library in ASP.NET Core</title>
      <description>&lt;p&gt;In many cases you’ll want to ship MVC controllers, possibly views or taghelpers, etc… as part of your class library. To do this correctly you’ll want to add your assembly to ASP.NET’s “Application Parts” on startup. Its quite simple to do but you might want to make sure you are not enabling all sorts of services that the user of your library doesn’t need.&lt;/p&gt;
&lt;p&gt;The common way to do this on startup is to have your own extension method to “Add” your class library to the services. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;public static class MyLibStartup
{
    public static IServiceCollection AddMyLib(this IServiceCollection services)
    {
        //TODO: Add your own custom services to DI

        //Add your assembly to the ASP.NET application parts
        var builder = services.AddMvc();
        builder.AddApplicationPart(typeof(MyLibStartup).Assembly);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will work, but the call to &lt;a rel="noopener" href="https://github.com/aspnet/Mvc/blob/master/src/Microsoft.AspNetCore.Mvc/MvcServiceCollectionExtensions.cs#L26" target="_blank"&gt;AddMvc() is doing a lot more than you might think&lt;/a&gt; &lt;em&gt;(also note in &lt;/em&gt;&lt;a rel="noopener" href="https://github.com/aspnet/AspNetCore/blob/master/src/Mvc/Mvc/src/MvcServiceCollectionExtensions.cs#L27" target="_blank"&gt;&lt;em&gt;ASP.NET Core 3, it’s doing a similar amount&lt;/em&gt;&lt;/a&gt;&lt;em&gt; of work). &lt;/em&gt;This call is adding all of the services to the application required for: authorization, controllers, views, taghelpers, razor, api explorer, CORS, and more… This might be fine if your library requires all of these things but otherwise unless the user of your library also wants all of these things, in my opinion it’s probably better to only automatically add the services that you know your library needs.&lt;/p&gt;
&lt;p&gt;In order to add your assembly application part you need a reference to &lt;em&gt;IMvcBuilder&lt;/em&gt; which you can resolve by calling any number of the extension methods to add the services you need. Depending on what your application requires will depend on what services you’ll want to add. It’s probably best to start with the lowest common feature-set which is a call to AddMvcCore(), the updated code might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;//Add your assembly to the ASP.NET application parts
var builder = services.AddMvcCore();
builder.AddApplicationPart(typeof(MyLibStartup).Assembly);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From there you can add the other bits you need, for example, maybe you also need CORS:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;//Add your assembly to the ASP.NET application parts
var builder = services.AddMvcCore().AddCors();
builder.AddApplicationPart(typeof(MyLibStartup).Assembly);&lt;/code&gt;&lt;/pre&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:09:29 Z</pubDate>
      <a10:updated>2023-03-23T15:09:29Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1280</guid>
      <link>https://shazwazza.com/post/umbraco-passwords-and-aspnet-machine-keys/</link>
      <category>ASP.Net</category>
      <category>Umbraco</category>
      <title>Umbraco passwords and ASP.NET Machine Keys</title>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update!&lt;/strong&gt; I’ve updated a few points below in bold and have corrected a few things too&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This blog post is the result of a thread on Twitter which starts here: &lt;a href="https://twitter.com/crumpled_jeavon/status/880522105795870720" title="https://twitter.com/crumpled_jeavon/status/880522105795870720"&gt;https://twitter.com/crumpled_jeavon/status/880522105795870720&lt;/a&gt; and works its way into confusion. Suffice to say I can’t answer these questions in 140 chars so here’s re-cap in the form of Q and A about Machine Keys and Umbraco. Please note that I am not an expert in hashing algorithms, some of these answers are based on my own research. Hope this clears things up!&lt;/p&gt;
&lt;h3&gt;How is the password hashed?&lt;/h3&gt;
&lt;p&gt;It is &lt;a rel="noopener noreferrer" href="https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/src/Umbraco.Core/Security/MembershipProviderBase.cs#L671" target="_blank"&gt;hashed in the same way&lt;/a&gt; that the ASP.NET Universal membership provider (&lt;a rel="noopener noreferrer" href="https://www.nuget.org/packages/Microsoft.AspNet.Providers.Core/" target="_blank"&gt;DefaultMembershipProvider&lt;/a&gt;) and &lt;a rel="noopener noreferrer" href="https://referencesource.microsoft.com/#System.Web/Security/SQLMembershipProvider.cs,f37d42faca2b921e" target="_blank"&gt;SqlMembershipProvider&lt;/a&gt; hashes passwords which by default uses the HMACSHA256 algorithm.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://twitter.com/jschoemaker1984?lang=en" target="_blank"&gt;Jeffrey Schoemaker&lt;/a&gt; has been discussing updating Umbraco’s default password hashing to use an even stronger hash algorithm and I’ve recently &lt;a rel="noopener noreferrer" href="http://issues.umbraco.org/issue/U4-10089" target="_blank"&gt;updated a new task on the issue tracker&lt;/a&gt; to research this but it really comes down to the fact that Microsoft does not offer the best stronger hashing method in it’s standard .NET Framework so we’ll see where we end up.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update &lt;/strong&gt;– we will be shipping umbraco with stronger password hashing, possibly in a 7.7.x release &lt;/em&gt;&lt;a href="http://issues.umbraco.org/issue/U4-10089" title="http://issues.umbraco.org/issue/U4-10089"&gt;&lt;em&gt;http://issues.umbraco.org/issue/U4-10089&lt;/em&gt;&lt;/a&gt;&lt;em&gt; and it will use HMACSHA1 + PBKDF2 which is what ASP.NET Identity uses by default.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Is the Machine Key used for password hashing?&lt;/h3&gt;
&lt;p&gt;Yes, In 7.6.0+ it is by default because &lt;a rel="noopener noreferrer" href="https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/src/Umbraco.Web.UI/web.Template.config#L252" target="_blank"&gt;useLegacyEncoding&lt;/a&gt; is false by default in this release. Previous to 7.6.0 the &lt;a rel="noopener noreferrer" href="https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/src/Umbraco.Web.UI/web.Template.config#L252" target="_blank"&gt;useLegacyEncoding&lt;/a&gt; value was true by default purely to preserve some backwards compatibility settings for other products but you’ve been able to set it to true from 7.0.0 (IIRC). Since those products support the more secure password formats, this is now false by default and should be kept as false for new installs. By default the hashing algorithm is HMACSHA256 which &lt;span style="text-decoration: line-through;"&gt;uses&lt;/span&gt; comes from the ASP.NET Machine Key &lt;span style="text-decoration: line-through;"&gt;to perform part of it’s hashing function&lt;/span&gt; ‘validation’ algorithm type. This ‘validation’ algorithm type is configurable via the Machine Key or it is &lt;a rel="noopener noreferrer" href="https://msdn.microsoft.com/en-us/library/system.web.security.membership.hashalgorithmtype(v=vs.110).aspx" target="_blank"&gt;configurable&lt;/a&gt; at the membership provider level which would override the algorithm specified in the Machine Key, but you really shouldn’t change this to be a lesser strength hashing algorithm.&lt;/p&gt;
&lt;p&gt;The &lt;a rel="noopener noreferrer" href="https://msdn.microsoft.com/en-us/library/system.security.cryptography.hmac(v=vs.110).aspx" target="_blank"&gt;HMAC&lt;/a&gt; part of this algorithm means it’s derived from a &lt;a rel="noopener noreferrer" href="https://msdn.microsoft.com/en-us/library/system.security.cryptography.keyedhashalgorithm(v=vs.110).aspx" target="_blank"&gt;keyed algorithm&lt;/a&gt; and uses a key to generate the hash &lt;span style="text-decoration: line-through;"&gt;and the machine key is used to create this key by default. There doesn’t seem to be any documentation or reference to this online that I can find but trying to look through the crypto source code (which isn’t nice to look at) it seems that the default key gets set based on some logic in the &lt;/span&gt;&lt;a rel="noopener noreferrer" href="https://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider(v=vs.110).aspx" target="_blank"&gt;&lt;span style="text-decoration: line-through;"&gt;RSACryptoServiceProvider&lt;/span&gt;&lt;/a&gt;&lt;span style="text-decoration: line-through;"&gt; class which reads some info from the machine key.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update &lt;/strong&gt;– the key used to hash the passwords is the generated salt we produce it is not the key part of the Machine Key. This logic is exactly the same as the logic used in the (&lt;/em&gt;&lt;a rel="noopener noreferrer" href="https://www.nuget.org/packages/Microsoft.AspNet.Providers.Core/" target="_blank"&gt;&lt;em&gt;DefaultMembershipProvider&lt;/em&gt;&lt;/a&gt;&lt;em&gt;) and &lt;/em&gt;&lt;a rel="noopener noreferrer" href="https://referencesource.microsoft.com/#System.Web/Security/SQLMembershipProvider.cs,f37d42faca2b921e" target="_blank"&gt;&lt;em&gt;SqlMembershipProvider&lt;/em&gt;&lt;/a&gt;&lt;em&gt; and if the hashing algorithm key length doesn’t equal the generated salt key length then it is padded/trimmed to the correct length, see source &lt;/em&gt;&lt;a href="http://bit.ly/2uEXeUo"&gt;&lt;em&gt;here&lt;/em&gt;&lt;/a&gt;&lt;em&gt;. The part of the Machine Key that is used to hash the passwords is specifically the &lt;u&gt;algorithm type&lt;/u&gt;. As you can see on &lt;a href="http://www.a2zmenu.com/utility/Machine-Key-Generator.aspx"&gt;this machine key generator&lt;/a&gt;, there can be a handful of different algorithm types used for the ‘validation’ part of a machine key and the default of this value changes based on the ASP.NET version being used. In ASP.NET 4.5 the default is HMACSHA256. Also note that in ASP.NET 4.5 the following algorithms are &lt;a href="https://blogs.msdn.microsoft.com/webdev/2012/10/23/cryptographic-improvements-in-asp-net-4-5-pt-2/"&gt;no longer allowed&lt;/a&gt; in the Machine Key config: &lt;em&gt;AES&lt;/em&gt;, &lt;em&gt;3DES&lt;/em&gt;, and &lt;em&gt;MD5&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Do machine keys change between environments?&lt;/h3&gt;
&lt;p&gt;If you explicitly generate and set your own machine key in your web.config then the answer is &lt;strong&gt;No.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you don’t explicitly generate and set your own machine key than you will get an auto-generated machine key. The simple answer for this is: &lt;strong&gt;In most cases No&lt;/strong&gt;, an auto-generated machine key will not change between environments.&lt;/p&gt;
&lt;p&gt;To understand when it will change between environments is a little more complicated and comes down to a combination of IIS user, IIS website virtual path (i.e. if you are running a site in a virtual directory), and a combination of a few settings set at the machine config level: “IsolateApps” and “IsolateByAppId”. Unless a server administrator specifically changes these settings than chances are you won’t be affected by different auto-generated machine keys for your site. If you are really keen, here’s a series all about this topic and other cryptographic changes in .NET 4.5:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel="noopener noreferrer" href="https://blogs.msdn.microsoft.com/webdev/2012/10/22/cryptographic-improvements-in-asp-net-4-5-pt-1/" target="_blank"&gt;Part 1&lt;/a&gt; – see the “A brief digression: auto-generated machine keys” for info on auto-generating keys&lt;/li&gt;
&lt;li&gt;&lt;a rel="noopener noreferrer" href="https://blogs.msdn.microsoft.com/webdev/2012/10/23/cryptographic-improvements-in-asp-net-4-5-pt-2/" target="_blank"&gt;Part 2&lt;/a&gt; – in-depth info about the machine key and hashing changes&lt;/li&gt;
&lt;li&gt;&lt;a rel="noopener noreferrer" href="https://blogs.msdn.microsoft.com/webdev/2012/10/24/cryptographic-improvements-in-asp-net-4-5-pt-3/" target="_blank"&gt;Part 3&lt;/a&gt; – interesting info especially with regards to PBKDF2 in .NET Framework&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update &lt;/strong&gt;– another reason a machine key may change between environments is based on which version of ASP.NET is running and what it’s default ‘validation’ algorithm type is&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Can I change my machine key?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style="text-decoration: line-through;"&gt;No.&lt;/span&gt; YES&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="text-decoration: line-through;"&gt;However,&lt;/span&gt; I realize In some cases you might need to change it or move from an auto-generated machine key to an explicit machine key. If that is the case there &lt;span style="text-decoration: line-through;"&gt;will&lt;/span&gt; may be &lt;span style="text-decoration: line-through;"&gt;a lot of&lt;/span&gt; some manual work you’ll need to do. If you simply just change the machine key or add an explicit one when you previously didn’t have one than your members/users &lt;span style="text-decoration: line-through;"&gt;will&lt;/span&gt; might not be able to log in! This really comes down to what hashing algorithm was used originally to hash the passwords and what hash algorithm is configured in your Machine Key. If you install or change a machine key to a different algorithm that was used to first hash your passwords, then your members/users will not be able to log in.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update&lt;/strong&gt; – Previously I was under the impression that the key in the hashing algorithm was directly affected by the key in the machine key but after some more investigation it is not actually the case. As mentioned in the above updates, the part of the Machine Key that is used in the password hashing is the algorithm type specified (if you haven’t overridden this algorithm type by specifying it directly on the membership provider). I’ve also detailed some of this investigation in this ticket: &lt;a href="http://issues.umbraco.org/issue/U4-10222" title="http://issues.umbraco.org/issue/U4-10222"&gt;http://issues.umbraco.org/issue/U4-10222&lt;/a&gt; &lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Can I change from useLegacyEncoding ‘true’ to ‘false’?&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Not easily.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This will require a bit of extra work just like the above question but it’s not quite as tricky as changing a Machine Key. When you have useLegacyEncoding=’true’ , the machine key validation algorithm is not used, so you could do something like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new password column in the database which will store the newly hashed password based on the new machine key validation algorithm&lt;/li&gt;
&lt;li&gt;When a user logs in you can check if they have the new password format or not.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;If not, then you can validate the password based on the old format then re-hash with the new format and store it and delete the old stored hash password.&lt;/li&gt;
&lt;li&gt;If so, then you can validate the password based on the new format&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;To do this would probably require inheriting from the current Umbraco membership provider and implementing this logic yourself&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Do I need to generate and store a custom Machine Key if I am planning on Load Balancing?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Yes.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is also mentioned in the &lt;a rel="noopener noreferrer" href="https://our.umbraco.org/documentation/Getting-Started/Setup/Server-Setup/load-balancing/#asp-net-configuration" target="_blank"&gt;Umbraco docs&lt;/a&gt; for load balancing&lt;/p&gt;
&lt;h3&gt;Do I need to generate and install a Machine Key before I run the Umbraco installer?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Yes.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is because during the Umbraco installation it will hash and store the password for the admin account and if you then add a Machine Key after the fact, you will no longer be able to log in.&lt;/p&gt;
&lt;h3&gt;Can Umbraco generate a custom Machine Key on install for me?&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Yes&lt;/em&gt;!&lt;/p&gt;
&lt;p&gt;but it doesn’t do that right now. I created that &lt;a rel="noopener noreferrer" href="https://github.com/umbraco/Umbraco-CMS/pull/783" target="_blank"&gt;functionality in a PR&lt;/a&gt; but we decided to remove the machine key generation part when it was accepted. We have decided to bring it back though so that will be part of an upcoming Umbraco release, whether that is the 7.6.x series or 7.7 series is still being decided.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update &lt;/strong&gt;– this is shipping with 7.7 and new installs will have a Machine Key generated and installed for you. You can of course opt out of this in the advanced installer options if you like.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Do Machine Key’s exist in ASP.NET Core?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;No.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Well sort of but not really. I haven’t been able to research too much into this but when speaking to a couple MS Dev’s about this a couple years ago the answer was that the way machine key’s work will be different. If keys need to be shared between apps in ASP.NET Core the data protection APIs need to be used and these keys would then be stored in the registry or the Cloud (i.e. Azure Key Vault), here’s a &lt;a rel="noopener noreferrer" href="https://stackoverflow.com/a/42117903/694494" target="_blank"&gt;SO article on this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Clear as mud?! ;)&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:31 Z</pubDate>
      <a10:updated>2023-03-23T15:08:31Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1245</guid>
      <link>https://shazwazza.com/post/smidge-20-alpha-is-out/</link>
      <category>ASP.Net</category>
      <title>Smidge 2.0 alpha is out</title>
      <description>&lt;style&gt;
div.nuget-badge p code {
background: none;
background-color: #202020 !important;
border: 4px solid silver !important;
border-bottom-left-radius: 5px 5px !important;
border-bottom-right-radius: 5px 5px !important;
border-top-left-radius: 5px 5px !important;
border-top-right-radius: 5px 5px !important;
color: #e2e2e2 !important;
display: block !important;
font: normal normal normal 1.5em/normal 'andale mono', 'lucida console', monospace !important;
line-height: 1.5em !important;
overflow: auto !important;
padding: 15px !important;
}
&lt;/style&gt; &lt;img title="ASP.NET-Core-Logo_2colors_RGB_bitmap_MEDIUM" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="ASP.NET-Core-Logo_2colors_RGB_bitmap_MEDIUM" src="http://shazwazza.com/media/articulate/windows-live-writer-smidge-20-alpha-is-out_abc7-aspnet-core-logo_2colors_rgb_bitmap_medium_5.png" width="198" height="57"&gt;  &lt;p&gt;What is Smidge? Smidge is a lightweight &lt;u&gt;runtime &lt;/u&gt;bundling library (&lt;em&gt;CSS/JavaScript file minification, combination, compression&lt;/em&gt;) for ASP.NET Core. &lt;/p&gt; &lt;p&gt;&lt;img style="float: left; margin: 10px 20px 10px 0px; display: inline" src="https://raw.githubusercontent.com/Shazwazza/Smidge/master/assets/logo2.png" align="left"&gt;If you’ve come from ASP.NET 4.5 you would have been familiar with the &lt;a href="https://www.asp.net/mvc/overview/performance/bundling-and-minification" target="_blank"&gt;bundling/minification API&lt;/a&gt; and other bundling options like &lt;a href="https://github.com/shazwazza/clientdependency" target="_blank"&gt;ClientDependency&lt;/a&gt;, but that is no longer available in ASP.NET Core, instead it is advised to do all the bundling and pre-processing that you need as part of your build process …which certainly makes sense! So why create this library? A few reasons: some people just want to have a very simple bundling library and don’t want to worry about Gulp or Grunt or WebPack, in a lot of cases the overhead of runtime processing is not going to make any difference, and lastly, if you have created something like a CMS that dynamically loads in assets from 3rd party packages or plugins, you need a runtime bundler since these things don’t exist at build time.&lt;/p&gt; &lt;p&gt;Over the past few months I’ve been working on some enhancements to Smidge and have found a bit of time to get an &lt;a href="https://github.com/Shazwazza/Smidge/releases/tag/2.0.0-alpha" target="_blank"&gt;alpha released&lt;/a&gt;.&amp;nbsp; There’s loads of great new features in &lt;strong&gt;Smidge 2.0&lt;/strong&gt;! You can install via Nuget and is targets .NET Standard 1.6 and .NET Framework 4.5.2&lt;/p&gt; &lt;div class="nuget-badge"&gt; &lt;p&gt;&lt;code&gt;PM&amp;gt; Install-Package Smidge -Pre&lt;/code&gt;&lt;/p&gt;&lt;/div&gt; &lt;h2&gt;New to Smidge?&lt;/h2&gt; &lt;p&gt;It’s easy to get started with Smidge and there’s &lt;a href="https://github.com/Shazwazza/Smidge" target="_blank"&gt;lots of docs available on GitHub&lt;/a&gt; that cover installation, configuration, creating bundles and rendering&amp;nbsp; them.&lt;/p&gt; &lt;h2&gt;New Features&lt;/h2&gt; &lt;p&gt;Here’s a list of new features complete with lots of code examples&lt;/p&gt; &lt;h3&gt;Customizable Debug and Production options&lt;/h3&gt; &lt;p&gt;&lt;a title="https://github.com/Shazwazza/Smidge/issues/58" href="https://github.com/Shazwazza/Smidge/issues/58"&gt;&lt;font size="2"&gt;https://github.com/Shazwazza/Smidge/issues/58&lt;/font&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Previous to version 2.0, you could only configure aspects of the Production options and the Debug assets that were returned were just the raw static files. With 2.0, you have full control over how your assets are processed in both Debug and Production configurations. For example, if you wanted you could have your assets combined but not minified in Debug mode. This will also allow for non native web assets such as TypeScript to have pre-processors running and able to work in Debug mode. &lt;/p&gt; &lt;p&gt;Example:&lt;/p&gt;&lt;pre class="csharpcode"&gt;services.AddSmidge(_config)
    .Configure&amp;lt;SmidgeOptions&amp;gt;(options =&amp;gt;
    {
        &lt;span class="rem"&gt;//set the default e-tag options for Debug mode&lt;/span&gt;
        options.DefaultBundleOptions.DebugOptions.CacheControlOptions.EnableETag = &lt;span class="kwrd"&gt;false&lt;/span&gt;        
    });&lt;/pre&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;h3&gt;Fluent syntax for declaring/configuring bundles&lt;/h3&gt;
&lt;p&gt;&lt;a title="https://github.com/Shazwazza/Smidge/issues/55" href="https://github.com/Shazwazza/Smidge/issues/55"&gt;&lt;font size="2"&gt;https://github.com/Shazwazza/Smidge/issues/55&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you want to customize Debug or Production options per bundle, you can do so with a fluent syntax, for example:&lt;/p&gt;&lt;pre class="csharpcode"&gt;app.UseSmidge(bundles =&amp;gt;
{                
    &lt;span class="rem"&gt;//For this bundle, enable composite files for Debug mode, enable the file watcher so any changes&lt;/span&gt;
    &lt;span class="rem"&gt;//to the files are automatically re-processed and cache invalidated, disable cache control headers&lt;/span&gt;
    &lt;span class="rem"&gt;//and use a custom cache buster. You could of course use the .ForProduction options too &lt;/span&gt;
    bundles.Create(&lt;span class="str"&gt;"test-bundle-2"&lt;/span&gt;, WebFileType.Js, &lt;span class="str"&gt;"~/Js/Bundle2"&lt;/span&gt;)
        .WithEnvironmentOptions(BundleEnvironmentOptions.Create()
                .ForDebug(builder =&amp;gt; builder
                    .EnableCompositeProcessing()
                    .EnableFileWatcher()
                    .SetCacheBusterType&amp;lt;AppDomainLifetimeCacheBuster&amp;gt;()
                    .CacheControlOptions(enableEtag: &lt;span class="kwrd"&gt;false&lt;/span&gt;, cacheControlMaxAge: 0))
                .Build()
        );                
});&lt;/pre&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;h3&gt;Customizable Cache Buster&lt;/h3&gt;
&lt;p&gt;&lt;a title="https://github.com/Shazwazza/Smidge/issues/51" href="https://github.com/Shazwazza/Smidge/issues/51"&gt;&lt;font size="2"&gt;https://github.com/Shazwazza/Smidge/issues/51&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In version 1.0 the only cache busting mechanism was Smidge’s version property which is set in config, in 2.0 Smidge allows you to control how cache busting is controlled at a global and bundle level. 2.0 ships with 2 &lt;em&gt;ICacheBuster&lt;/em&gt; types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;ConfigCacheBuster &lt;/em&gt;– the default and uses Smidge’s version property in config&lt;/p&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;AppDomainLifetimeCacheBuster &lt;/em&gt;– if enabled will mean that the server/browser cache will be invalidated on every app domain recycle&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;If you want a different behavior, you can define you own &lt;em&gt;ICacheBuster&lt;/em&gt; add it to the IoC container and then just use it globally or per bundle. For example:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;//Set a custom MyCacheBuster as the default one for Debug assets:&lt;/span&gt;
services.AddSmidge(_config)
    .Configure&amp;lt;SmidgeOptions&amp;gt;(options =&amp;gt;
    {
        options.DefaultBundleOptions.DebugOptions.SetCacheBusterType&amp;lt;MyCustomCacheBuster&amp;gt;();       
    });

&lt;span class="rem"&gt;//Set a custom MyCacheBuster as the cache buster for a particular bundle in debug mode:&lt;/span&gt;
bundles.Create(&lt;span class="str"&gt;"test-bundle-2"&lt;/span&gt;, WebFileType.Js, &lt;span class="str"&gt;"~/Js/Bundle2"&lt;/span&gt;)
    .WithEnvironmentOptions(BundleEnvironmentOptions.Create()
            .ForDebug(builder =&amp;gt; builder
                .SetCacheBusterType&amp;lt;MyCacheBuster&amp;gt;()
            .Build()
    );&lt;/pre&gt;
&lt;h3&gt;Customizable cache headers&lt;/h3&gt;
&lt;p&gt;&lt;a title="https://github.com/Shazwazza/Smidge/issues/48" href="https://github.com/Shazwazza/Smidge/issues/48"&gt;&lt;font size="2"&gt;https://github.com/Shazwazza/Smidge/issues/48&lt;/font&gt;&lt;/a&gt;&lt;font size="2"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;You can now control if you want the &lt;em&gt;ETag&lt;/em&gt; header output and you can control the value set for max-age/s-maxage/Expires header at a global or bundle level, for example:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;//This would set the max-age header for this bundle to expire in 5 days&lt;/span&gt;
bundles.Create(&lt;span class="str"&gt;"test-bundle-5"&lt;/span&gt;, WebFileType.Js, &lt;span class="str"&gt;"~/Js/Bundle5"&lt;/span&gt;)
    .WithEnvironmentOptions(BundleEnvironmentOptions.Create()
            .ForProduction(builder =&amp;gt; builder                                
                .CacheControlOptions(enableEtag: &lt;span class="kwrd"&gt;true&lt;/span&gt;, cacheControlMaxAge: (5 * 24)))
            .Build()
    );&lt;/pre&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;h3&gt;Callback to customize the pre-processor pipeline per web file&lt;/h3&gt;
&lt;p&gt;&lt;a title="https://github.com/Shazwazza/Smidge/issues/59" href="https://github.com/Shazwazza/Smidge/issues/59"&gt;&lt;font size="2"&gt;https://github.com/Shazwazza/Smidge/issues/59&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is handy in case you want to modify the pipeline for a given web file at runtime based on some criteria, for example:&lt;/p&gt;&lt;pre class="csharpcode"&gt;services.AddSmidge(_config)
    .Configure&amp;lt;SmidgeOptions&amp;gt;(options =&amp;gt;
    {
        &lt;span class="rem"&gt;//set the callback&lt;/span&gt;
        options.PipelineFactory.OnGetDefault = GetDefaultPipelineFactory;
    });

&lt;span class="rem"&gt;//The GetDefaultPipeline method could do something like modify the default pipeline to use Nuglify for JS processing:&lt;/span&gt;

&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; PreProcessPipeline GetDefaultPipelineFactory(WebFileType fileType, IReadOnlyCollection&amp;lt;IPreProcessor&amp;gt; processors)
{
    &lt;span class="kwrd"&gt;switch&lt;/span&gt; (fileType)
    {
        &lt;span class="kwrd"&gt;case&lt;/span&gt; WebFileType.Js:
            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; PreProcessPipeline(&lt;span class="kwrd"&gt;new&lt;/span&gt; IPreProcessor[]
            {
                processors.OfType&amp;lt;NuglifyJs&amp;gt;().Single()
            });                
    }
    &lt;span class="rem"&gt;//returning null will fallback to the logic defined in the registered PreProcessPipelineFactory&lt;/span&gt;
    &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;null&lt;/span&gt;;
}

&lt;/pre&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;h3&gt;File watching with automatic cache invalidation&lt;/h3&gt;
&lt;p&gt;&lt;a title="https://github.com/Shazwazza/Smidge/pull/42" href="https://github.com/Shazwazza/Smidge/pull/42"&gt;&lt;font size="2"&gt;https://github.com/Shazwazza/Smidge/pull/42&lt;/font&gt;&lt;/a&gt;&lt;font size="2"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;During the development process it would be nice to be able to test composite files but have them auto re-process and invalidate the cache whenever one of the source files changes… in 2.0 this is possible!&amp;nbsp; You can enable file watching at the global level or per bundle. Example:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;//Enable file watching for all files in this bundle when in Debug mode&lt;/span&gt;
bundles.Create(&lt;span class="str"&gt;"test-bundle-7"&lt;/span&gt;,
    &lt;span class="kwrd"&gt;new&lt;/span&gt; CssFile(&lt;span class="str"&gt;"~/Js/Bundle7/a1.js"&lt;/span&gt;),
    &lt;span class="kwrd"&gt;new&lt;/span&gt; CssFile(&lt;span class="str"&gt;"~/Js/Bundle7/a2.js"&lt;/span&gt;))
    .WithEnvironmentOptions(BundleEnvironmentOptions.Create()
            .ForDebug(builder =&amp;gt; builder.EnableFileWatcher())
            .Build()
    );&lt;/pre&gt;
&lt;h2&gt;What’s next?&lt;/h2&gt;
&lt;p&gt;This is an alpha release since there’s a few things that I need to complete. Most are already done but I just need to make Nuget packages for them:&lt;/p&gt;
&lt;h4&gt;&lt;/h4&gt;
&lt;h3&gt;More pre-processors&lt;/h3&gt;
&lt;p&gt;I’ve enabled support for a &lt;a href="https://github.com/xoofx/NUglify" target="_blank"&gt;Nuglify&lt;/a&gt; pre-processor for both CSS and JS (Nuglify is a fork of the &lt;a href="http://ajaxmin.codeplex.com/"&gt;Microsoft Ajax Minifier&lt;/a&gt; for ASP.NET Core + additional features). I also enabled support for an Uglify NodeJs pre-processor which uses &lt;a href="https://github.com/aspnet/JavaScriptServices" target="_blank"&gt;Microsoft.AspNetCore.NodeServices&lt;/a&gt; to invoke Node.js from ASP.NET and run the JS version of Uglify. I just need to get these on Nuget but haven’t got around to that yet.&lt;/p&gt;
&lt;h3&gt;A quick note on minifier performance &lt;/h3&gt;
&lt;p&gt;Though Nuglify and Uglify have a better minification engine (better/smarter size reduction) than JsMin because they create an &lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree" target="_blank"&gt;AST (Abstract Syntax Tree)&lt;/a&gt; to perform it’s processing, they are actually much slower and consume more resources than JsMin. Since Smidge is a Runtime bundling engine, its generally important to ensure that the bundling/minification is performed quickly. Smidge has strict caching so the bundling/minification will only happen once (depending on your &lt;em&gt;ICacheBuster&lt;/em&gt; you are using) but it is still recommended to understand the performance implications of replacing JsMin with another minifier. &lt;a href="https://github.com/Shazwazza/Smidge/wiki/Custom-pre-processing#minification-benchmarks" target="_blank"&gt;I’ve put together some benchmarks&lt;/a&gt; (NOTE: a smaller Minified % is better):&lt;/p&gt;
&lt;table style="box-sizing: border-box; overflow: auto; margin-bottom: 16px; font-family: ; white-space: normal; word-spacing: 0px; margin-top: 0px; border-collapse: collapse; text-transform: none; color: ; border-spacing: 0px; orphans: 2; widows: 2; display: block; letter-spacing: normal; background-color: rgb(255,255,255); text-indent: 0px; font-variant-ligatures: normal; font-variant-caps: normal; -webkit-text-stroke-width: 0px" width="700"&gt;
&lt;thead style="box-sizing: border-box"&gt;
&lt;tr style="box-sizing: border-box; border-top: rgb(204,204,204) 1px solid; background-color: rgb(255,255,255)"&gt;
&lt;th style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;Method&lt;/font&gt;&lt;/font&gt;&lt;/th&gt;
&lt;th style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;Median&lt;/font&gt;&lt;/font&gt;&lt;/th&gt;
&lt;th style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;StdDev&lt;/font&gt;&lt;/font&gt;&lt;/th&gt;
&lt;th style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;Scaled&lt;/font&gt;&lt;/font&gt;&lt;/th&gt;
&lt;th style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;Scaled-SD&lt;/font&gt;&lt;/font&gt;&lt;/th&gt;
&lt;th style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;Minified %&lt;/font&gt;&lt;/font&gt;&lt;/th&gt;
&lt;th style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;Gen 0&lt;/font&gt;&lt;/font&gt;&lt;/th&gt;
&lt;th style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;Gen 1&lt;/font&gt;&lt;/font&gt;&lt;/th&gt;
&lt;th style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;Gen 2&lt;/font&gt;&lt;/font&gt;&lt;/th&gt;
&lt;th style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;Bytes Allocated/Op&lt;/font&gt;&lt;/font&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody style="box-sizing: border-box"&gt;
&lt;tr style="box-sizing: border-box; border-top: rgb(204,204,204) 1px solid; background-color: rgb(255,255,255)"&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;JsMin&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;10.2008 ms&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;0.3102 ms&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;1.00&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;0.00&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;51.75%&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;-&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;-&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;-&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;155,624.67&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="box-sizing: border-box; border-top: rgb(204,204,204) 1px solid; background-color: rgb(248,248,248)"&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;Nuglify&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;69.0778 ms&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;0.0180 ms&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;6.72&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;0.16&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;32.71%&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;53.00&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;22.00&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;15.00&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;4,837,313.07&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="box-sizing: border-box; border-top: rgb(204,204,204) 1px solid; background-color: rgb(255,255,255)"&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;JsServicesUglify&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;1,548.3951 ms&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;7.6388 ms&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;150.95&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;3.73&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;32.63%&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;0.97&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;-&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;-&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td style="box-sizing: border-box; border-top: rgb(221,221,221) 1px solid; border-right: rgb(221,221,221) 1px solid; border-bottom: rgb(221,221,221) 1px solid; padding-bottom: 6px; padding-top: 6px; padding-left: 13px; border-left: rgb(221,221,221) 1px solid; padding-right: 13px"&gt;&lt;font face="Segoe UI"&gt;&lt;font style="font-size: 12pt" color="#333333"&gt;576,056.55&lt;/font&gt;&lt;/font&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;The last benchmark may be a bit misleading because the processing is done via NodeJs which executes in a separate process so I'm unsure if the actual memory usage of that can be properly captured by &lt;a href="https://github.com/dotnet/BenchmarkDotNet"&gt;BenchmarkDotNet&lt;/a&gt; but you can see it's speed is much slower. 
&lt;h2&gt;Thanks!&lt;/h2&gt;
&lt;p&gt;Big thanks to &lt;a href="https://github.com/dazinator" target="_blank"&gt;@dazinator&lt;/a&gt; for all the help, recommendations, testing, feedback, etc… and for the rest of the community for filing bugs, questions, and comments. Much appreciated :)&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:19 Z</pubDate>
      <a10:updated>2023-03-23T15:08:19Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1253</guid>
      <link>https://shazwazza.com/post/aspnet-core-application-shutdown-events/</link>
      <category>ASP.Net</category>
      <title>ASP.NET Core application shutdown events</title>
      <description>&lt;p&gt;While porting an existing library to ASP.NET Core I had to find the equivalent functionality of &lt;a href="https://msdn.microsoft.com/en-us/library/system.web.hosting.iregisteredobject%28v=vs.110%29.aspx?f=255&amp;amp;MSPPError=-2147217396" target="_blank"&gt;&lt;em&gt;IRegisteredObject&lt;/em&gt;&lt;/a&gt; which I use for graceful shutdowns of running tasks in background threads. The newer &amp;amp; nicer approach to this in ASP.NET Core is &lt;a href="https://github.com/aspnet/Hosting/blob/dev/src/Microsoft.AspNetCore.Hosting.Abstractions/IApplicationLifetime.cs" target="_blank"&gt;&lt;em&gt;Microsoft.AspNetCore.Hosting.IApplicationLifetime&lt;/em&gt;&lt;/a&gt;:&lt;/p&gt;&lt;pre class="csharpcode"&gt;  &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
  &lt;span class="rem"&gt;/// Allows consumers to perform cleanup during a graceful shutdown.&lt;/span&gt;
  &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;interface&lt;/span&gt; IApplicationLifetime
  {
    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// Triggered when the application host has fully started and is about to wait&lt;/span&gt;
    &lt;span class="rem"&gt;/// for a graceful shutdown.&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    CancellationToken ApplicationStarted { get; }

    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// Triggered when the application host is performing a graceful shutdown.&lt;/span&gt;
    &lt;span class="rem"&gt;/// Requests may still be in flight. Shutdown will block until this event completes.&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    CancellationToken ApplicationStopping { get; }

    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// Triggered when the application host is performing a graceful shutdown.&lt;/span&gt;
    &lt;span class="rem"&gt;/// All requests should be complete at this point. Shutdown will block&lt;/span&gt;
    &lt;span class="rem"&gt;/// until this event completes.&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    CancellationToken ApplicationStopped { get; }

    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;Requests termination the current application.&amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;void&lt;/span&gt; StopApplication();
  }&lt;/pre&gt;
&lt;p&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/p&gt;
&lt;p&gt;Unlike the old &lt;em&gt;IRegisteredObject&lt;/em&gt; this interface is pretty clear on it’s functionality. &lt;/p&gt;
&lt;p&gt;Registering a method to be called for any of the three operations is simple:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;//register the application shutdown handler&lt;/span&gt;
 applicationLifetime.ApplicationStopping.Register(DisposeResources);

&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; DisposeResources()
{
    &lt;span class="rem"&gt;//Cleanup stuff when the app is shutting down&lt;/span&gt;
}&lt;/pre&gt;
&lt;p&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/p&gt;
&lt;p&gt;Obtaining an instance of &lt;em&gt;IApplicationLifetime&lt;/em&gt; can be done during &lt;em&gt;Startup.cs&lt;/em&gt; in the &lt;em&gt;Configure&lt;/em&gt; method&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Configure(IApplicationBuilder app, IApplicationLifetime applicationLifetime)
{
    &lt;span class="rem"&gt;// start your app&lt;/span&gt;
}&lt;/pre&gt;
&lt;p&gt;Happy coding!
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:15 Z</pubDate>
      <a10:updated>2023-03-23T15:08:15Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1226</guid>
      <link>https://shazwazza.com/post/installing-net-core-101-on-ubuntu-1610/</link>
      <category>ASP.Net</category>
      <title>Installing .NET Core 1.01 on Ubuntu 16.10</title>
      <description>&lt;h3&gt;TL;DR&amp;nbsp; You’ll need to manually install libicu55 &lt;/h3&gt; &lt;h3&gt;&lt;a href="http://shazwazza.com/media/articulate/windows-live-writer-installing-net-core-101-on-ubuntu-110_2d9-image_2.png"&gt;&lt;img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; float: right; padding-top: 0px; padding-left: 0px; border-left: 0px; margin: 0px 0px 10px 10px; display: inline; padding-right: 0px" border="0" alt="image" src="http://shazwazza.com/media/articulate/windows-live-writer-installing-net-core-101-on-ubuntu-110_2d9-image_thumb.png" width="557" align="right" height="214"&gt;&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: Linux noob content below&lt;/p&gt; &lt;p&gt;I’ve been testing out some .NET Core cross platform stuff and originally had been using Ubuntu 14.04 with .NET Core 1.0.0 and that all worked fine along with the installation instructions from &lt;a href="https://www.microsoft.com/net/core#ubuntu"&gt;https://www.microsoft.com/net/core#ubuntu&lt;/a&gt; , however some of the latest tests I’ve been doing needed a MySQL version later than 5.5. It would seem that when I installed MySQL on Ubuntu 14.04 by executing &lt;em&gt;apt-get mysql-server&lt;/em&gt; that I got 5.5 which was not compatible with what I needed. So attempting to upgrade gave me other issues for which I would require a later version of Ubuntu. Long story short, I’m a linux noob and I couldn’t get anything to upgrade, ended up executing all sorts of commands I didn’t understand and probably shouldn’t have and ultimately killed my Linux install.&lt;/p&gt; &lt;p&gt;So a clean install of Ubuntu 16.04 it was … there’s a catch though, you can choose between LTS (Long Term Support) or not. I chose not to since It’s a VM and I don’t mind newer updates, etc… Turns out that was a bad idea with .NET Core installs! It would seem that once the non LTS is installed you end up with 16.10 which has installed some newer versions of required libraries, namely something called libicu which is now on 57 instead of a required 55.&lt;/p&gt; &lt;p&gt;Trying to run the normal installation procedure from the web instructions mentioned above for 16.04 ended up telling me this:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;em&gt;&lt;font color="#666666"&gt;sudo apt-get install dotnet-dev-1.0.0-preview2-003131&lt;/font&gt;&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;font color="#666666"&gt;…&lt;/font&gt;&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;font color="#666666"&gt;Some packages could not be installed. This may mean that you have&lt;br&gt;requested an impossible situation or if you are using the unstable&lt;br&gt;distribution that some required packages have not yet been created&lt;br&gt;or been moved out of Incoming.&lt;br&gt;The following information may help to resolve the situation:&lt;/font&gt;&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;font color="#666666"&gt;The following packages have unmet dependencies:&lt;br&gt;dotnet-dev-1.0.0-preview2-003131 : Depends: dotnet-sharedframework-microsoft.netcore.app-1.0.1 but it is not going to be installed &lt;br&gt;E: Unable to correct problems, you have held broken packages.&lt;/font&gt;&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;So what the heck does that mean?! So after some Googling, I tried to just install the dependency:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;em&gt;&lt;font color="#666666"&gt;sudo apt-get install dotnet-sharedframework-microsoft.netcore.app-1.0.1&lt;/font&gt;&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;font color="#666666"&gt;…&lt;/font&gt;&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;font color="#666666"&gt;Some packages could not be installed. This may mean that you have&lt;br&gt;requested an impossible situation or if you are using the unstable&lt;br&gt;distribution that some required packages have not yet been created&lt;br&gt;or been moved out of Incoming.&lt;br&gt;The following information may help to resolve the situation:&lt;/font&gt;&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;font color="#666666"&gt;The following packages have unmet dependencies:&lt;br&gt;dotnet-sharedframework-microsoft.netcore.app-1.0.1 : Depends: libicu55 (&amp;gt;=55.1.1~) but it is not installable &lt;br&gt;E: Unable to correct problems, you have held broken packages.&lt;/font&gt;&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Ok, not much further but I gather that I need libicu55 installed, so let’s try:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;em&gt;&lt;font color="#666666"&gt;sudo apt-get install libicu55&lt;/font&gt;&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;font color="#666666"&gt;…&lt;/font&gt;&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;font color="#666666"&gt;Package libicu55 is not available, but is referred to by another package.&lt;br&gt;This may mean that the package is missing, has been obsoleted, or&lt;br&gt;is only available from another source&lt;/font&gt;&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;font color="#666666"&gt;E: Package libicu55 has no installation candidate&lt;/font&gt;&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;Manual libicu55 installation&lt;/h2&gt; &lt;p&gt;I suppose normal linux users would probably just know that you need to download and install libicu55 manually. Well it took a little bit of research for me to figure that out, but here’s what to do:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;head over to &lt;a href="http://packages.ubuntu.com/en/xenial/amd64/libicu55/download"&gt;http://packages.ubuntu.com/en/xenial/amd64/libicu55/download&lt;/a&gt;  &lt;li&gt;click one of the mirror links to download the file  &lt;li&gt;in Terminal, head to the folder you downloaded it (i.e. probably ~/Downloads)  &lt;li&gt;install it using this command: &lt;em&gt;sudo dpkg –i libicu55_55.1-7_amd64.deb&lt;/em&gt;&amp;nbsp; (or whatever file name you saved it as)&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;That should install just fine, then you can run &lt;em&gt;sudo apt-get install dotnet-dev-1.0.0-preview2-003131 &lt;/em&gt;and everything will be fine again :)&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:15 Z</pubDate>
      <a10:updated>2023-03-23T15:08:15Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1153</guid>
      <link>https://shazwazza.com/post/all-about-aspnet-file-change-notification-fcn/</link>
      <category>ASP.Net</category>
      <title>All about ASP.Net File Change Notification (FCN)</title>
      <description>&lt;p&gt;There’s a chance you might not have heard of FCN (File Change Notification) in ASP.Net and there’s an even bigger chance you didn’t realize how much it might affect you. &lt;/p&gt; &lt;h2&gt;What is FCN in ASP.Net? &lt;/h2&gt; &lt;p&gt;As you know ASP.Net monitors a few files/folders such as the ~/web.config and ~/App_Code and will restart your app domain when it detects changes. This is part of FCN in ASP.Net but it goes much deeper than that. There are a few other files &amp;amp; folders that ASP.Net monitors which will also cause an app domain restart: bin, App_WebReferences, App_GlobalResources, App_Code, Global.asax, &lt;a href="http://shazwazza.com/post/taming-the-buildmanager-aspnet-temp-files-and-appdomain-restarts/" target="_blank"&gt;and others&lt;/a&gt;. However what you might not realize is that ASP.Net actually monitors every single folder (+ files) in your web app! &lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;u&gt;Update 29/07/2016!&lt;/u&gt; I found a &lt;/em&gt;&lt;a href="https://blogs.msdn.microsoft.com/tmarq/2007/11/01/asp-net-file-change-notifications-exactly-which-files-and-directories-are-monitored/" target="_blank"&gt;&lt;em&gt;nice MS article about FCN here&lt;/em&gt;&lt;/a&gt;&lt;em&gt;. I’ve been looking for more resources about this since a few new issues have been cropping up and I’m wondering if another MS update has caused another problem. Recently we’ve seen an increase in the error: “Overwhelming Change Notification in …” which has everything to do with FCN. I’ve also added a few links to the bottom of this post.&lt;/em&gt;&lt;/p&gt; &lt;h2&gt;Why do I care?&lt;/h2&gt; &lt;p&gt;If you have a web app that contains a lot of folders there is a good chance that the performance of your file system is affected in one way or another. To give you an example of how many file system watchers ASP.Net generates I &lt;a href="https://github.com/Shazwazza/UmbracoScripts/blob/master/src/Web/ASPNetFileMonitorList.cshtml" target="_blank"&gt;created a Razor view (.cshtml)&lt;/a&gt;&amp;nbsp; (&lt;strong&gt;which you should definitely test on your own site!&lt;/strong&gt;) to display what is actually happening behind the scenes. Here’s the output for the first run page from the Visual Studio 2015 MVC template site:&lt;/p&gt; &lt;p&gt;&lt;a href="http://shazwazza.com/media/articulate/windows-live-writer-all-abonet-file-change-notification-fcn_8fe4-image_2.png"&gt;&lt;img width="640" height="360" title="image" style="display: inline;" alt="image" src="http://shazwazza.com/media/articulate/windows-live-writer-all-abonet-file-change-notification-fcn_8fe4-image_thumb.png"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;The table above lists all of the “&lt;em&gt;DirectoryMonitor&lt;/em&gt;” instances and the folder they are attached to along with all of the “&lt;em&gt;FileMonitor&lt;/em&gt;” instances attached to that &lt;em&gt;DirectoryMonitor&lt;/em&gt;. It’s worth nothing that these are not simply just .Net’s &lt;em&gt;FileSystemWatcher&lt;/em&gt;, but some sort of native windows IO using a delegate called &lt;em&gt;NativeFileChangeNotification&lt;/em&gt;. The above table doesn’t seem too scary but these monitors are not static either, they grow with every directory and file that is accessed in your web app.&amp;nbsp; For example, if I navigate to these pages: /Home/About, /Home/Contact, /Account/Register, /Account/Login and go back to view this table it looks like:&lt;/p&gt; &lt;p&gt;&lt;a href="http://shazwazza.com/media/articulate/windows-live-writer-all-abonet-file-change-notification-fcn_8fe4-image_4.png"&gt;&lt;img width="640" height="277" title="image" style="display: inline;" alt="image" src="http://shazwazza.com/media/articulate/windows-live-writer-all-abonet-file-change-notification-fcn_8fe4-image_thumb_1.png"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Things start to get interesting when you have a web application that has a lot of folders. Some examples of this might be:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;You are using a dynamic image processor such as &lt;a href="http://imageresizing.net/" target="_blank"&gt;Image Resizer&lt;/a&gt; or &lt;a href="http://imageprocessor.org/" target="_blank"&gt;Image Processor&lt;/a&gt; since these will create a lot of folders based on hashes to store these dynamic images  &lt;li&gt;Maybe you have a lot of members/users on your site and you have one or more folders for each one  &lt;li&gt;Maybe you use nodejs or bower and you store these generated folders in your web root and references the assets directly in those folders … there can be &lt;strong&gt;tons&lt;/strong&gt; of folders in &lt;em&gt;bower_components&lt;/em&gt; and &lt;em&gt;node_modules&lt;/em&gt;  &lt;li&gt;You could be using a web framework or CMS like Umbraco or Orchard (I’m sure there are plenty of others) that contain quite a lot of folders by default&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Here’s the truncated result of an Orchard site after visiting the admin area, creating a page and displaying it:&lt;/p&gt; &lt;p&gt;&lt;a href="http://shazwazza.com/media/articulate/windows-live-writer-all-abonet-file-change-notification-fcn_8fe4-image_6.png"&gt;&lt;img width="640" height="243" title="image" style="display: inline;" alt="image" src="http://shazwazza.com/media/articulate/windows-live-writer-all-abonet-file-change-notification-fcn_8fe4-image_thumb_2.png"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Whoa! &lt;/p&gt; &lt;h2&gt;ASP.Net’s FCNMode&lt;/h2&gt; &lt;p&gt;At some stage in ASP.Net’s lifetime somebody must have complained about this to Microsoft and they released a hotfix (seen here: &lt;a title="https://support.microsoft.com/en-us/kb/911272" href="https://support.microsoft.com/en-us/kb/911272"&gt;https://support.microsoft.com/en-us/kb/911272&lt;/a&gt;). Then I can only assume that other people complained about this and with .Net 4.5 a new configuration setting appeared: &lt;a href="https://msdn.microsoft.com/en-us/library/system.web.configuration.httpruntimesection.fcnmode(v=vs.110).aspx" target="_blank"&gt;FCNMode&lt;/a&gt;. &lt;a href="https://msdn.microsoft.com/en-us/library/system.web.configuration.fcnmode(v=vs.110).aspx#" target="_blank"&gt;This documentation&lt;/a&gt; explains a little about what each option does:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Default - For each subdirectory, the application creates an object that monitors the subdirectory. This is the default behavior  &lt;li&gt;Disabled - File change notification is disabled.  &lt;li&gt;NotSet - File change notification is not set, so the application creates an object that monitors each subdirectory. This is the default behavior.  &lt;li&gt;Single - The application creates one object to monitor the main directory and uses this object to monitor each subdirectory.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Unfortunately these docs don’t tell us the whole story. It’s obvious that Default/NotSet are the same thing and are the default. Disabled is fairly clear but what it doesn’t mention is that if you set it to Disabled, this will disable all FCN for your web app, so if you change the contents of /bin or /App_Code, the site will not restart. However, Disabled still means that the web.config file is monitored so if you use this setting you can still bump your web.config to restart your site. &lt;/p&gt; &lt;p&gt;What exactly is “&lt;em&gt;Single&lt;/em&gt;” though?&lt;/p&gt; &lt;p&gt;The folks over at DNN seem to have quite a bit of experience with FCN and &lt;a href="http://www.dnnsoftware.com/community-blog/cid/154980/aspnet-file-change-notifications-and-dnn" target="_blank"&gt;this article&lt;/a&gt; seems to be the only place that actually explains “Single” mode correctly: &lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;em&gt;“FCNMode creates a monitor object with a buffer size of 4KB for each folder. When FCNMode is set to Single, a single monitor object is created with a buffer size of 64KB. When there are file changes, the buffer is filled with file change information. If the buffer gets overwhelmed with too many file change notifications an “Overwhelming File Change Notifications” error will occur and the app domain will recycle. The likelihood of the buffer getting overwhelmed is higher in an environment where you are using separate file server because the folder paths are much larger.”&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;If I change fcnMode=”Single” in my web.config for the same Orchard site above, the results are:&lt;/p&gt; &lt;p&gt;&lt;a href="http://shazwazza.com/media/articulate/windows-live-writer-all-abonet-file-change-notification-fcn_8fe4-image_8.png"&gt;&lt;img width="640" height="203" title="image" style="display: inline;" alt="image" src="http://shazwazza.com/media/articulate/windows-live-writer-all-abonet-file-change-notification-fcn_8fe4-image_thumb_3.png"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;That’s quite a bit less! &lt;/p&gt; &lt;p&gt;I’m sure there are pros to using “&lt;em&gt;Default&lt;/em&gt;” instead of “&lt;em&gt;Single&lt;/em&gt;” but I’m not actually sure what they are. &lt;/p&gt; &lt;h2&gt;Real world problem&lt;/h2&gt; &lt;p&gt;The circumstance where “&lt;em&gt;Default&lt;/em&gt;” FCN mode becomes a real problem is when you have a website that is running off of a remote file share.&amp;nbsp; As noted in Shawn Walker’s DNN article mentioned above:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;em&gt;“The likelihood of the buffer getting overwhelmed is higher in an environment where you are using separate file server because the folder paths are much larger.”&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;I can’t actually see a performance difference between &lt;em&gt;Default&lt;/em&gt; or &lt;em&gt;Single&lt;/em&gt; when running locally with an SSD HD, but I have seen some big issues running with &lt;em&gt;Default&lt;/em&gt; when hosting on a remote file server:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Constant app domain restarts – I’ve seen constant app domain restarts even when a site is just serving requests without any IO activity apart from just reading. And by ‘Constant’ … I mean every few seconds all day long.  &lt;li&gt;File server performance suffers severely – When multiple sites are active and are being hosted on a remote file server with &lt;em&gt;Default &lt;/em&gt;FCNMode, the writing performance of the file server is drastically degraded even though when monitoring &lt;a href="https://en.wikipedia.org/wiki/IOPS" target="_blank"&gt;IOPS&lt;/a&gt; there doesn’t appear to be any issues&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;To solve this issue we changed fcnMode=”Single” in the machine.config so that all sites would effectively use “Single”… and the result was instant: No more constant app restarts, file server performance was instantly back to normal. And as far as I can tell, there has been no downside to running FCNMode in &lt;em&gt;Single.&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;So I really wonder what the up-side of Default is when it seems that running in Single mode is perfectly stable running on any hosting environment… ?&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;FCN doesn’t appear to be a thing with aspnetcore!&lt;/em&gt;&lt;/p&gt; &lt;h2&gt;More info?&lt;/h2&gt; &lt;p&gt;&lt;em&gt;Updated 13/08/2018 – &lt;/em&gt;I wrote an FCN Viewer tool and wrote some more in-depth info about FCN and more of it’s quirks here: &lt;a title="https://shazwazza.com/post/fcn-file-change-notification-viewer-for-aspnet/" href="https://shazwazza.com/post/fcn-file-change-notification-viewer-for-aspnet/"&gt;https://shazwazza.com/post/fcn-file-change-notification-viewer-for-aspnet/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Here’s a list of helpful links about FCN in ASP.Net:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a title="http://imageresizing.net/docs/fcnmode" href="http://imageresizing.net/docs/fcnmode"&gt;http://imageresizing.net/docs/fcnmode&lt;/a&gt;  &lt;li&gt;&lt;a title="https://support.microsoft.com/en-us/kb/911272" href="https://support.microsoft.com/en-us/kb/911272"&gt;https://support.microsoft.com/en-us/kb/911272&lt;/a&gt;  &lt;li&gt;&lt;a title="http://stackoverflow.com/questions/17615193/azure-websites-appdomain-many-restarts" href="http://stackoverflow.com/questions/17615193/azure-websites-appdomain-many-restarts"&gt;http://stackoverflow.com/questions/17615193/azure-websites-appdomain-many-restarts&lt;/a&gt;  &lt;li&gt;&lt;a title="http://blogs.msdn.com/b/toddca/archive/2005/12/01/499144.aspx" href="http://blogs.msdn.com/b/toddca/archive/2005/12/01/499144.aspx"&gt;http://blogs.msdn.com/b/toddca/archive/2005/12/01/499144.aspx&lt;/a&gt;  &lt;li&gt;&lt;a title="http://www.dnnsoftware.com/community-blog/cid/154980/aspnet-file-change-notifications-and-dnn" href="http://www.dnnsoftware.com/community-blog/cid/154980/aspnet-file-change-notifications-and-dnn"&gt;http://www.dnnsoftware.com/community-blog/cid/154980/aspnet-file-change-notifications-and-dnn&lt;/a&gt;  &lt;li&gt;&lt;a title="https://msdn.microsoft.com/en-us/library/system.web.configuration.fcnmode(v=vs.110).aspx" href="https://msdn.microsoft.com/en-us/library/system.web.configuration.fcnmode(v=vs.110).aspx"&gt;https://msdn.microsoft.com/en-us/library/system.web.configuration.fcnmode(v=vs.110).aspx&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;em&gt;Updated 29/07/2016&lt;/em&gt;! – More links I’ve discovered. Turns out this has been an issue for IIS + ASP.Net for quite some time with various older hotfixes, some of these new links might shed some light on the particular problem you might be having.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a title="https://blogs.msdn.microsoft.com/tess/2006/08/02/asp-net-case-study-lost-session-variables-and-appdomain-recycles/" href="https://blogs.msdn.microsoft.com/tess/2006/08/02/asp-net-case-study-lost-session-variables-and-appdomain-recycles/"&gt;https://blogs.msdn.microsoft.com/tess/2006/08/02/asp-net-case-study-lost-session-variables-and-appdomain-recycles/&lt;/a&gt; &lt;li&gt;&lt;a title="https://blogs.msdn.microsoft.com/tmarq/2007/11/01/asp-net-file-change-notifications-exactly-which-files-and-directories-are-monitored/" href="https://blogs.msdn.microsoft.com/tmarq/2007/11/01/asp-net-file-change-notifications-exactly-which-files-and-directories-are-monitored/"&gt;https://blogs.msdn.microsoft.com/tmarq/2007/11/01/asp-net-file-change-notifications-exactly-which-files-and-directories-are-monitored/&lt;/a&gt; &lt;li&gt;&lt;a title="https://support.microsoft.com/en-us/kb/3052480" href="https://support.microsoft.com/en-us/kb/3052480"&gt;https://support.microsoft.com/en-us/kb/3052480&lt;/a&gt; &lt;li&gt;&lt;a title="https://support.microsoft.com/en-us/kb/913297" href="https://support.microsoft.com/en-us/kb/913297"&gt;https://support.microsoft.com/en-us/kb/913297&lt;/a&gt; &lt;li&gt;&lt;a title="https://support.microsoft.com/en-us/kb/920970" href="https://support.microsoft.com/en-us/kb/920970"&gt;https://support.microsoft.com/en-us/kb/920970&lt;/a&gt; &lt;li&gt;&lt;a title="https://multitiered.wordpress.com/2010/08/11/sitecore-oms-error-overwhelming-change-notification/" href="https://multitiered.wordpress.com/2010/08/11/sitecore-oms-error-overwhelming-change-notification/"&gt;https://multitiered.wordpress.com/2010/08/11/sitecore-oms-error-overwhelming-change-notification/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:15 Z</pubDate>
      <a10:updated>2023-03-23T15:08:15Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1171</guid>
      <link>https://shazwazza.com/post/appveyor-and-aspnet-core-previously-aspnet-5/</link>
      <category>ASP.Net</category>
      <title>AppVeyor and ASP.Net Core (Previously ASP.Net 5)</title>
      <description>&lt;p&gt;Last year I created a runtime Js/Css pre-processor for ASP.Net Core (Previously ASP.Net 5) called “&lt;a href="https://github.com/Shazwazza/Smidge" target="_blank"&gt;Smidge&lt;/a&gt;” and have been meaning to blog about how I integrated this with AppVeyor – to run my tests, build the project and output the Nuget files I need, so here it goes…&lt;/p&gt; &lt;h2&gt;The build script&lt;/h2&gt; &lt;p&gt;I use Powershell for my build scripts for my projects since it’s reasonably easy to read and the same script format has worked quite well for ASP.Net Core projects too. You can see the whole build file &lt;a href="https://github.com/Shazwazza/Smidge/blob/master/build.ps1" target="_blank"&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;. Here’s the important things to note:&lt;/p&gt; &lt;p&gt;With AppVeyor (and probably other build servers), you need to ensure that it actually has the dnx version you need: &lt;/p&gt;&lt;pre class="csharpcode"&gt;# ensure the correct version
&amp;amp; $DNVM install 1.0.0-rc1-update1&lt;/pre&gt;
&lt;p&gt;Next you need to make sure that the current process is using the version you need to build:&lt;/p&gt;&lt;pre class="csharpcode"&gt;# use the correct version
&amp;amp; $DNVM use 1.0.0-rc1-update1&lt;/pre&gt;
&lt;p&gt;Then we need to use DNU to make sure that your project has everything it needs to build:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&amp;amp; $DNU restore &lt;span class="str"&gt;"$ProjectJsonPath"&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Lastly it’s just building and packaging the project:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&amp;amp; $DNU build &lt;span class="str"&gt;"$ProjectJsonPath"&lt;/span&gt;
&amp;amp; $DNU pack &lt;span class="str"&gt;"$ProjectJsonPath"&lt;/span&gt; --configuration Release --&lt;span class="kwrd"&gt;out&lt;/span&gt; &lt;span class="str"&gt;"$ReleaseFolder"&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;The rest of the build file is normal Powershell bits. &lt;/p&gt;
&lt;h2&gt;The test script&lt;/h2&gt;
&lt;p&gt;I’m using xunit for unit tests in this project and similarly to the build script I’m using a simple Powershell script to execute the tests on the build server, the test runner file is &lt;a href="https://github.com/Shazwazza/Smidge/blob/master/tests.ps1" target="_blank"&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;. The important parts are just like the above: Ensure the correct version is installed and being used by the current process and making sure that the project has everything it needs to build and finally to build it. The last missing piece is to actually run the tests:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&amp;amp; $DNX -p &lt;span class="str"&gt;"$TestsFolder"&lt;/span&gt; test&lt;/pre&gt;
&lt;p&gt;Where ‘&lt;em&gt;test’&lt;/em&gt; is a command defined in my &lt;a href="https://github.com/Shazwazza/Smidge/blob/master/tests/Smidge.Tests/project.json#L10" target="_blank"&gt;project.json as part of my unit test project&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;AppVeyor configuration&lt;/h2&gt;
&lt;p&gt;The good news is that there’s really not a lot to setup, it’s super easy. In your AppVeyor settings just go to the ‘Build’ section and tell it to execute the Powershell script with its build version information:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://shazwazza.com/media/articulate/windows-live-writer-smidge-rc3-released_eb4d-image_2.png"&gt;&lt;img title="image" style="display: inline" alt="image" src="http://shazwazza.com/media/articulate/windows-live-writer-smidge-rc3-released_eb4d-image_thumb.png" width="600" height="208"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then for the unit tests is basically the same, click on the ‘Tests’ section and tell it to execute the Powershell test script:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://shazwazza.com/media/articulate/windows-live-writer-smidge-rc3-released_eb4d-image_4.png"&gt;&lt;img title="image" style="display: inline" alt="image" src="http://shazwazza.com/media/articulate/windows-live-writer-smidge-rc3-released_eb4d-image_thumb_1.png" width="600" height="222"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And that’s pretty much it! The only other part I’ve had to setup is the paths to my Artifacts (Nuget files) based on the current build number.&lt;/p&gt;
&lt;p&gt;Now whenever I commit, AppVeyor will execute the build script and test script and we can see the output:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://shazwazza.com/media/articulate/windows-live-writer-smidge-rc3-released_eb4d-image_6.png"&gt;&lt;img title="image" style="display: inline" alt="image" src="http://shazwazza.com/media/articulate/windows-live-writer-smidge-rc3-released_eb4d-image_thumb_2.png" width="600" height="238"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And it’s smart enough to know that the test runner executed is for unit tests, so all the unit test output shows up in the ‘Tests’ tab of the build&lt;/p&gt;
&lt;p&gt;&lt;a href="http://shazwazza.com/media/articulate/windows-live-writer-smidge-rc3-released_eb4d-image_8.png"&gt;&lt;img title="image" style="display: inline" alt="image" src="http://shazwazza.com/media/articulate/windows-live-writer-smidge-rc3-released_eb4d-image_thumb_3.png" width="600" height="152"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now that that’s all setup, AppVeyor even gives you a handy Nuget feed that you can use to test your packages based on each build, this can be configured on the ‘NuGet’ settings section, for example here’s the Smidge feed: &lt;a title="https://ci.appveyor.com/nuget/smidge" href="https://ci.appveyor.com/nuget/smidge"&gt;https://ci.appveyor.com/nuget/smidge&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Smidge 1.0.0-RC3&lt;/h2&gt;
&lt;p&gt;It’s worth noting that I’ve also released a new version of Smidge since I finally had some time to work on it. Couple of bugs fixed in this release and also a handy new feature too! You can see the release notes here: &lt;a title="https://github.com/Shazwazza/Smidge/releases/tag/1.0.0-rc3" href="https://github.com/Shazwazza/Smidge/releases/tag/1.0.0-rc3"&gt;https://github.com/Shazwazza/Smidge/releases/tag/1.0.0-rc3&lt;/a&gt;.&amp;nbsp; I’ve also updated a lot of the documentation, the main readme file was getting quite long so I’ve moved all of the docs over to the project’s Wiki on GitHub and condensed the readme for the most important bits. Have a look here: &lt;a title="https://github.com/Shazwazza/Smidge/releases/tag/1.0.0-rc3" href="https://github.com/Shazwazza/Smidge/releases/tag/1.0.0-rc3"&gt;https://github.com/Shazwazza/Smidge&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:15 Z</pubDate>
      <a10:updated>2023-03-23T15:08:15Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1328</guid>
      <link>https://shazwazza.com/post/custom-assembly-loading-with-aspnet-core/</link>
      <category>ASP.Net</category>
      <title>Custom Assembly loading with Asp.Net Core</title>
      <description>&lt;p&gt;Building a plugin system in Asp.Net Core is a dream compared to previous Asp.Net versions! &lt;/p&gt; &lt;p&gt;In previous versions it was not really feasible to load Assemblies located outside of the /bin folder &lt;em&gt;&lt;u&gt;for a web application&lt;/u&gt;&lt;/em&gt;. &lt;a href="http://shazwazza.com/post/developing-a-plugin-framework-in-aspnet-with-medium-trust/" target="_blank"&gt;I battled with this concept quite a long time ago&lt;/a&gt; and although it’s sort of possible, the notion of having a plugin system that supported loading DLLs from outside of the /bin folder was riddled with hacks/problems and not really supported OOTB. &lt;/p&gt; &lt;p&gt;A large part of the issues has to do with something called an ‘Assembly Load Context’. In traditional .Net there are 3 of these context types: “&lt;strong&gt;Load&lt;/strong&gt;”, “&lt;strong&gt;LoadFrom&lt;/strong&gt;” and “&lt;strong&gt;Neither&lt;/strong&gt;”, &lt;a href="http://blogs.msdn.com/b/suzcook/archive/2003/05/29/57143.aspx" target="_blank"&gt;here’s a very old but very relevant post about these contexts&lt;/a&gt; from Suzanne Cook. In traditional Asp.Net, the “Load” context is used as the default context and it is managed by something called &lt;em&gt;Fusion&lt;/em&gt; (.Net’s normal Assembly Loader/Binder). The problem with this context is that it is difficult to load an assembly into it that isn’t located in &lt;em&gt;Fusion’s &lt;/em&gt;probing paths (i.e. /bin folder). If you load in an Assembly with a different Assembly Load Context and then try to mix it’s Types with the Types from the default context&amp;nbsp; … you’ll quickly see that it’s not going to work. &lt;/p&gt; &lt;h2&gt;The “Neither” context&lt;/h2&gt; &lt;p&gt;Here is the Neither context definition as defined by Suzanne Cook:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;em&gt;If the user generated or found the assembly instead of Fusion, it's in neither context. This applies to assemblies loaded by Assembly.Load(byte[]) and Reflection Emit assemblies (that haven't been loaded from disk). &lt;/em&gt;&lt;a href="http://blogs.msdn.com/suzcook/archive/2003/09/19/57248.aspx"&gt;&lt;em&gt;Assembly.LoadFile()&lt;/em&gt;&lt;/a&gt;&lt;em&gt; assemblies are also generally loaded into this context, even though a path is given (because it doesn't go through Fusion).&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;In Asp.Net Core (targeting CoreCLR), the default Assembly Load Context is the “Neither” context. This is a flexible context because it doesn’t use &lt;em&gt;Fusion&lt;/em&gt; and&amp;nbsp; it allows for loading assemblies any way that you want - including loading an assembly from a byte array, from a path or by a name. Since all of Asp.Net Core uses this context it means that all of the types loaded in with this context can talk to each other without having the previous Asp.Net problems.&lt;/p&gt; &lt;p&gt;I would assume that Asp.Net Core targeting Desktop CLR would still operate the same as before and still have the 3 types of Assembly Load Context’s … Maybe someone over at Microsoft can elaborate on that one? (David Fowler… surely you know? :)&lt;/p&gt; &lt;h2&gt;Finding referenced plugin assemblies&lt;/h2&gt; &lt;p&gt;In many cases if you create a product that supports plugin types, developers will create plugins for your product and ship them via Nuget. This is a pretty standard approach since it allows developers that are using your product to install plugins from the Nuget command line or from within Visual Studio. In this case plugin types will be found in referenced assemblies to your application and will be automatically loaded. Asp.Net Core has an interface called &lt;em&gt;Microsoft.Extensions.PlatformAbstractions.ILibraryManager&lt;/em&gt; that can be used to resolve your application’s currently referenced ‘Libraries’ (i.e Nuget packages) and then each ‘Library’ returned exposes the Assemblies that it includes. Asp.Net MVC 6 has an even more helpful interface called &lt;em&gt;Microsoft.AspNet.Mvc.Infrastructure.IAssemblyProvider&lt;/em&gt; which returns a list of referenced assemblies that are filtered based on if they are assemblies that reference a subset of MVC assemblies. The default implementation of &lt;em&gt;IAssemblyProvider&lt;/em&gt; (&lt;em&gt;DefaultAssemblyProvider&lt;/em&gt;) is extensible and we can use it to override it’s property &lt;em&gt;ReferenceAssemblies&lt;/em&gt; in order to supply our own product assembly names instead of the MVC ones. This is perfect since this allows us to get a list of candidate assemblies that might contain plugins for your product:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ReferencePluginAssemblyProvider : DefaultAssemblyProvider
{
    &lt;span class="rem"&gt;//NOTE: The DefaultAssemblyProvider uses ILibraryManager to do the library/assembly querying&lt;/span&gt;
    &lt;span class="kwrd"&gt;public&lt;/span&gt; ReferencePluginAssemblyProvider(ILibraryManager libraryManager) : &lt;span class="kwrd"&gt;base&lt;/span&gt;(libraryManager)
    {
    }

    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; HashSet&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; ReferenceAssemblies 
        =&amp;gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; HashSet&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;(&lt;span class="kwrd"&gt;new&lt;/span&gt;[] {&lt;span class="str"&gt;"MyProduct.Web"&lt;/span&gt;, &lt;span class="str"&gt;"MyProduct.Core"&lt;/span&gt;});
}&lt;/pre&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;p&gt;now if you want to get a list of candidate assemblies that your application is referencing you could do:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;//returns all assemblies that reference your product Assemblies&lt;/span&gt;
var candidateReferenceAssemblies = referencedPluginAssemblyProvider.CandidateAssemblies;&lt;/pre&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;h2&gt;Finding and loading non-referenced plugin assemblies&lt;/h2&gt;
&lt;p&gt;This is where things get fun since this is the type of thing that wasn’t really very feasible with traditional Asp.Net web apps. Lets say you have a plugin framework where a plugin is installed via your web app, not in Visual Studio and therefore not directly referenced in your project. For this example, the plugin is a self contained collection of files and folders which could consist of: Css, JavaScript, Razor Views, and Assemblies. This plugin model is pretty nice since to install the plugin would mean just dropping the plugin folder into the right directory in your app and similarly&amp;nbsp; to uninstall it you can just remove the folder.&amp;nbsp; The first step is to be able to load in these plugin Assemblies from custom locations. For an example, let’s assume the web app has the following folder structure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;App Root 
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;App_Plugins &lt;/strong&gt;&lt;em&gt;&amp;lt;—This will be the directory that contains plugin folders&lt;/em&gt; 
&lt;ul&gt;
&lt;li&gt;MyPlugin1 
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;bin &lt;/strong&gt;&lt;em&gt;&amp;lt;—by convention we’ll search for Assemblies in the /bin folder inside of a plugin&lt;/em&gt; 
&lt;li&gt;Views&lt;/li&gt;&lt;/ul&gt;
&lt;li&gt;MyPlugin2 
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;bin&lt;em&gt; &lt;/em&gt;&lt;/strong&gt;&lt;em&gt;&amp;lt;—by convention we’ll search for Assemblies in the /bin folder inside of a plugin&lt;/em&gt; 
&lt;li&gt;css&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;li&gt;Views 
&lt;li&gt;&lt;em&gt;wwwroot&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;IAssemblyLoader&lt;/h3&gt;
&lt;p&gt;The first thing we need is an ‘&lt;em&gt;Microsoft.Extensions.PlatformAbstractions.IAssemblyLoader&lt;/em&gt;’, this is the thing that will do the assembly loading into the Assembly Load Context based on an &lt;em&gt;AssemblyName &lt;/em&gt;and a location of a DLL:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; DirectoryLoader : IAssemblyLoader
{
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; IAssemblyLoadContext _context;
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; DirectoryInfo _path;

    &lt;span class="kwrd"&gt;public&lt;/span&gt; DirectoryLoader(DirectoryInfo path, IAssemblyLoadContext context)
    {
        _path = path;
        _context = context;
    }

    &lt;span class="kwrd"&gt;public&lt;/span&gt; Assembly Load(AssemblyName assemblyName)
    {
        &lt;span class="kwrd"&gt;return&lt;/span&gt; _context.LoadFile(Path.Combine(_path.FullName, assemblyName.Name + &lt;span class="str"&gt;".dll"&lt;/span&gt;));
    }

    &lt;span class="kwrd"&gt;public&lt;/span&gt; IntPtr LoadUnmanagedLibrary(&lt;span class="kwrd"&gt;string&lt;/span&gt; name)
    {
        &lt;span class="rem"&gt;//this isn't going to load any unmanaged libraries, just throw&lt;/span&gt;
        &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; NotImplementedException();
    }
}&lt;/pre&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;h3&gt;IAssemblyProvider&lt;/h3&gt;
&lt;p&gt;Next up we’ll need a custom &lt;em&gt;IAssemblyProvider&lt;/em&gt; but instead of using the one MVC ships with, this one will be totally custom in order to load and resolve the assemblies based on the plugin’s /bin folders. The following code should be pretty straight forward, the &lt;em&gt;CandidateAssemblies&lt;/em&gt; property iterates over each found /bin folder inside of a plugin’s folder inside of App_Plugins. For each /bin folder found it creates a &lt;em&gt;DirectoryLoader &lt;/em&gt;mentioned above and loads in each DLL found by it’s &lt;em&gt;AssemblyName&lt;/em&gt; into the current Assembly Load Context.&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="rem"&gt;/// This will return assemblies found in App_Plugins plugin's /bin folders&lt;/span&gt;
&lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; CustomDirectoryAssemblyProvider : IAssemblyProvider
{
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; IFileProvider _fileProvider;
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; IAssemblyLoadContextAccessor _loadContextAccessor;
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; IAssemblyLoaderContainer _assemblyLoaderContainer;

    &lt;span class="kwrd"&gt;public&lt;/span&gt; CustomDirectoryAssemblyProvider(
            IFileProvider fileProvider, 
            IAssemblyLoadContextAccessor loadContextAccessor, 
            IAssemblyLoaderContainer assemblyLoaderContainer)
    {
        _fileProvider = fileProvider;
        _loadContextAccessor = loadContextAccessor;
        _assemblyLoaderContainer = assemblyLoaderContainer;
    }

    &lt;span class="kwrd"&gt;public&lt;/span&gt; IEnumerable&amp;lt;Assembly&amp;gt; CandidateAssemblies
    {
        get
        {
            var content = _fileProvider.GetDirectoryContents(&lt;span class="str"&gt;"/App_Plugins"&lt;/span&gt;);
            &lt;span class="kwrd"&gt;if&lt;/span&gt; (!content.Exists) &lt;span class="kwrd"&gt;yield&lt;/span&gt; &lt;span class="kwrd"&gt;break&lt;/span&gt;;
            &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (var pluginDir &lt;span class="kwrd"&gt;in&lt;/span&gt; content.Where(x =&amp;gt; x.IsDirectory))
            {
                var binDir = &lt;span class="kwrd"&gt;new&lt;/span&gt; DirectoryInfo(Path.Combine(pluginDir.PhysicalPath, &lt;span class="str"&gt;"bin"&lt;/span&gt;));
                &lt;span class="kwrd"&gt;if&lt;/span&gt; (!binDir.Exists) &lt;span class="kwrd"&gt;continue&lt;/span&gt;;
                &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (var assembly &lt;span class="kwrd"&gt;in&lt;/span&gt; GetAssembliesInFolder(binDir))
                {
                    &lt;span class="kwrd"&gt;yield&lt;/span&gt; &lt;span class="kwrd"&gt;return&lt;/span&gt; assembly;
                }
            }
        }
    }

    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// Returns assemblies loaded from /bin folders inside of App_Plugins&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;param name="binPath"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;private&lt;/span&gt; IEnumerable&amp;lt;Assembly&amp;gt; GetAssembliesInFolder(DirectoryInfo binPath)
    {
        &lt;span class="rem"&gt;// Use the default load context&lt;/span&gt;
        var loadContext = _loadContextAccessor.Default;

        &lt;span class="rem"&gt;// Add the loader to the container so that any call to Assembly.Load &lt;/span&gt;
        &lt;span class="rem"&gt;// will call the load context back (if it's not already loaded)&lt;/span&gt;
        &lt;span class="kwrd"&gt;using&lt;/span&gt; (_assemblyLoaderContainer.AddLoader(
            &lt;span class="kwrd"&gt;new&lt;/span&gt; DirectoryLoader(binPath, loadContext)))
        {
            &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (var fileSystemInfo &lt;span class="kwrd"&gt;in&lt;/span&gt; binPath.GetFileSystemInfos(&lt;span class="str"&gt;"*.dll"&lt;/span&gt;))
            {
                &lt;span class="rem"&gt;//// In theory you should be able to use Assembly.Load() here instead&lt;/span&gt;
                &lt;span class="rem"&gt;//var assembly1 = Assembly.Load(AssemblyName.GetAssemblyName(fileSystemInfo.FullName));&lt;/span&gt;
                var assembly2 = loadContext.Load(AssemblyName.GetAssemblyName(fileSystemInfo.FullName));
                &lt;span class="kwrd"&gt;yield&lt;/span&gt; &lt;span class="kwrd"&gt;return&lt;/span&gt; assembly2;
            }
        }
    }
}&lt;/pre&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;p&gt;That’s pretty much it! If you have an instance of &lt;em&gt;CustomDirectoryAssemblyProvider&lt;/em&gt; then you can get Assembly references to all of the assemblies found in App_Plugins:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;//returns all plugin assemblies found in App_Plugins&lt;/span&gt;
var candidatePluginAssemblies = customDirectoryAssemblyProvider.CandidateAssemblies;&lt;/pre&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;h2&gt;Integrating non-referenced plugins/Assemblies with MVC&lt;/h2&gt;
&lt;p&gt;What if you had custom plugin types as MVC Controllers or other MVC types? By default MVC only knows about assemblies that your project has references to based on the &lt;em&gt;DefaultAssemblyLoader&lt;/em&gt;.&amp;nbsp; If we wanted MVC to know about Controllers that exist in a plugin not referenced by your project (i.e. in App_Plugins) then it’s a case of registering a custom &lt;em&gt;IAssemblyProvider&lt;/em&gt; in IoC which will get resolved by MVC. To make this super flexible we can create a custom &lt;em&gt;IAssemblyProvider&lt;/em&gt; that wraps multiple other ones and allows you to pass in a custom &lt;em&gt;referenceAssemblies&lt;/em&gt; filter if you wanted to use this to resolve your own plugin types:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; CompositeAssemblyProvider : DefaultAssemblyProvider
{
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; IAssemblyProvider[] _additionalProviders;
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt;[] _referenceAssemblies;

    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// Constructor&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;param name="libraryManager"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;param name="additionalProviders"&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// If passed in will concat the assemblies returned from these &lt;/span&gt;
    &lt;span class="rem"&gt;/// providers with the default assemblies referenced&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;param name="referenceAssemblies"&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// If passed in it will filter the candidate libraries to ones&lt;/span&gt;
    &lt;span class="rem"&gt;/// that reference the assembly names passed in. &lt;/span&gt;
    &lt;span class="rem"&gt;/// (i.e. "MyProduct.Web", "MyProduct.Core" )&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;public&lt;/span&gt; CompositeAssemblyProvider(
        ILibraryManager libraryManager,
        IAssemblyProvider[] additionalProviders = &lt;span class="kwrd"&gt;null&lt;/span&gt;,
        &lt;span class="kwrd"&gt;string&lt;/span&gt;[] referenceAssemblies = &lt;span class="kwrd"&gt;null&lt;/span&gt;) : &lt;span class="kwrd"&gt;base&lt;/span&gt;(libraryManager)
    {
        _additionalProviders = additionalProviders;
        _referenceAssemblies = referenceAssemblies;
    }

    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// Uses the default filter if a custom list of reference&lt;/span&gt;
    &lt;span class="rem"&gt;/// assemblies has not been provided&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; HashSet&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; ReferenceAssemblies
        =&amp;gt; _referenceAssemblies == &lt;span class="kwrd"&gt;null&lt;/span&gt;
            ? &lt;span class="kwrd"&gt;base&lt;/span&gt;.ReferenceAssemblies
            : &lt;span class="kwrd"&gt;new&lt;/span&gt; HashSet&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;(_referenceAssemblies);
    
    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// Returns the base Libraries referenced along with any DLLs/Libraries&lt;/span&gt;
    &lt;span class="rem"&gt;/// returned from the custom IAssemblyProvider passed in&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; IEnumerable&amp;lt;Library&amp;gt; GetCandidateLibraries()
    {
        var baseCandidates = &lt;span class="kwrd"&gt;base&lt;/span&gt;.GetCandidateLibraries();
        &lt;span class="kwrd"&gt;if&lt;/span&gt; (_additionalProviders == &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;return&lt;/span&gt; baseCandidates;
        &lt;span class="kwrd"&gt;return&lt;/span&gt; baseCandidates               
            .Concat(
            _additionalProviders.SelectMany(provider =&amp;gt; provider.CandidateAssemblies.Select(
                x =&amp;gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; Library(x.FullName, &lt;span class="kwrd"&gt;null&lt;/span&gt;, Path.GetDirectoryName(x.Location), &lt;span class="kwrd"&gt;null&lt;/span&gt;, Enumerable.Empty&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;(),
                    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="kwrd"&gt;new&lt;/span&gt; AssemblyName(x.FullName) }))));
    }
}&lt;/pre&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;p&gt;To register this in IoC you just need to make sure it’s registered after you register MVC so that it overrides the last registered &lt;em&gt;IAssemblyProvider&lt;/em&gt;:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;//Add MVC services&lt;/span&gt;
services.AddMvc();
&lt;span class="rem"&gt;//Replace the default IAssemblyProvider with the composite one&lt;/span&gt;
services.AddSingleton&amp;lt;IAssemblyProvider, CompositeAssemblyProvider&amp;gt;(provider =&amp;gt;
{
    &lt;span class="rem"&gt;//create the custom plugin directory provider&lt;/span&gt;
    var hosting = provider.GetRequiredService&amp;lt;IApplicationEnvironment&amp;gt;();
    var fileProvider = &lt;span class="kwrd"&gt;new&lt;/span&gt; PhysicalFileProvider(hosting.ApplicationBasePath);
    var pluginAssemblyProvider = &lt;span class="kwrd"&gt;new&lt;/span&gt; CustomDirectoryAssemblyProvider(
        fileProvider,         
        PlatformServices.Default.AssemblyLoadContextAccessor,
        PlatformServices.Default.AssemblyLoaderContainer);
    &lt;span class="rem"&gt;//return the composite one - this wraps the default MVC one&lt;/span&gt;
    &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; CompositeAssemblyProvider(
        provider.GetRequiredService&amp;lt;ILibraryManager&amp;gt;(),
        &lt;span class="kwrd"&gt;new&lt;/span&gt; IAssemblyProvider[] {pluginAssemblyProvider});
});&lt;/pre&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Your all set! Now you have the ability to load in Assemblies from any location you want, you could even load them in as byte array’s from an external data source.&amp;nbsp; What’s great about all of this is that it just works and you can integrate these external Assemblies into MVC. &lt;/p&gt;
&lt;p&gt;Some things worth noting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Parts of the assembly loading APIs are changing a bit in Asp.Net Core RC2: &lt;a title="https://github.com/aspnet/Announcements/issues/149" href="https://github.com/aspnet/Announcements/issues/149"&gt;https://github.com/aspnet/Announcements/issues/149&lt;/a&gt; 
&lt;li&gt;The above code doesn’t take into account what happens if you load in the same Assembly from multiple locations. In this case, the last one in wins/is active AFAIK – I haven’t tested this yet but I’m pretty sure that’s how it works. 
&lt;li&gt;You may have some issues if load in the same Assembly more than once from multiple locations if those Assemblies have different strong names, or major versions applied to them – I also haven’t tested this yet&lt;/li&gt;&lt;/ul&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:15 Z</pubDate>
      <a10:updated>2023-03-23T15:08:15Z</a10:updated>
    </item>
  </channel>
</rss>