F# Weekly #19, 2021 – Bolero, Aardvark.Templates聽and README for NuGet packages

Welcome to F# Weekly,

A roundup of F# content from this past week:

News

Videos and Slides

Blogs

F# vNext

Highlighted projects

New Releases

That鈥檚 all for now. Have a great week.

If you want to help keep F# Weekly going, click here to jazz me with Coffee!

Buy Me A Coffeesubscribe_blog_button-02

 

F# Weekly #18, 2021 – Hawaii, End of .NET 4.6.1 and ‘inline if lambda’

Welcome to F# Weekly,

A roundup of F# content from this past week:

News

Videos and Slides

Blogs

F# vNext

Highlighted projects

New Releases

That鈥檚 all for now. Have a great week.

If you want to help keep F# Weekly going, click here to jazz me with Coffee!

Buy Me A Coffeesubscribe_blog_button-02

 

F# Weekly #17, 2021 – 64-bit Visual Studio 2022

Welcome to F# Weekly,

A roundup of F# content from this past week:

News

Videos and Slides

Blogs

F# vNext

Highlighted projects

New Releases

That鈥檚 all for now. Have a great week.

If you want to help keep F# Weekly going, click here to jazz me with Coffee!

Buy Me A Coffeesubscribe_blog_button-02

 

F# Weekly #16, 2021 – F# Day, SqlHydra, HODI and fodinfo

Welcome to F# Weekly,

A roundup of F# content from this past week:

News

Videos and Slides

Blogs

F# vNext

Highlighted projects

New Releases

That鈥檚 all for now. Have a great week.

If you want to help keep F# Weekly going, click here to jazz me with Coffee!

Buy Me A Coffeesubscribe_blog_button-02

 

F# Weekly #15, 2021 – .NET 6 Preview 3 and Rider 2021.1

Welcome to F# Weekly,

A roundup of F# content from this past week:

News

Videos and Slides

Blogs

F# vNext

Highlighted projects

New Releases

That鈥檚 all for now. Have a great week.

If you want to help keep F# Weekly going, click here to jazz me with Coffee!

Buy Me A Coffeesubscribe_blog_button-02

 

Announcing OpenXML Package Explorer for VS Code

I am excited to announce my new project “OpenXML Package Explorer” extension for Visual Studio Code.

It allows to explore content of Office Open XML packages (*.pptx, *.docx, *.xlsx) inside Visual Studio Code. You can open any valid OpenXML package in Tree View, explore parts inside package, their relationships and content of xml parts (pre-formatted xml with highlighting).

Extension is inspired by windows-only PackageExplorer (lost in CodePlex archive) and Open XML Package Editor for Modern Visual Studios (Visual Studio for Windows) but implemented on top of .NET 5 using F#, Fable, Fable.Remoting, and re-using some pieces from Ionide.

How it works

I believe that source code of this extension can be used as one more sample of Fable-powered VSCode extension. Here is how it currently works:

  1. F# source code compiled to JS using Fable 3 and bundled with webpack.
  2. Code that interacts with OpenXML packages is written in .NET and uses the latest version of System.IO.Packaging
  3. When extension is activated it starts .NET 5 process with API exposed using Fable.Remoting.
  4. Extension assumes that .NET 5 runtime is installed on user machine but it depends on .NET Install Tool for Extension Authors that should help to install runtime to users that do not have it yet.
  5. JS bundle runs inside Node.js VS Code Extension Host process.
  6. Fable.Remoting.Client cannot be used, because it is built on top of XMLHttpRequest that does not exist in Node.js world. Here is feature request for Fable.Remoting.NodeClient that may become a thing one day.
  7. Current client-server communication is built on top of axios library and Fable.Axios bindings with manually written client.
  8. Extension is created using LambdaFactory/fable-vscode-demo sample that uses ionide/ionide-vscode-helpers with bindings for VS Code Extension API.

P.S. Many thanks for Krzysztof Cie艣lak and Ionide team for ionide-vscode-fsharp, ionide-vscode-helpers and fable-vscode-demo and Stef Levesque for vscode-zipexplorer used as reference implementation and inspiration.

`dotnet watch` with Microsoft.Identity.Web or custom IDistributedCache

Microsoft.Identity.Web is new (GA from Sept 30, 2020) library which contains a set of reusable classes used in conjunction with ASP.NET Core for integrating with the Microsoft identity platform (formerly Azure AD v2.0 endpoint) and AAD B2C.

AzureAD/microsoft-identity-web

Microsoft.Identity.Web project template is included in .NET 5.0 with tutorials like “Create a Blazor Server app that uses the Microsoft identity platform for authentication“. The library is really nice and easy to use, but development experience is not ideal yet.

When you run server with `dotnet watch` you will see following errors after each restart.

MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call.

Microsoft.Identity.Client.Internal.Requests.Silent.SilentRequest.ExecuteAsync

MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent.

Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync

The reason is not obvious from the error message. In fact, you browser calls the server with .AspNetCore.Cookies cookies that server cannot accept and cannot renew. What to do? Easy – open dev tool, clean cookies, refresh the page, wait for next server restart and repeat it again. You won’t last long.

Workaround with custom IDistributedCache

In Startup.cs you most likely find code similar

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
        .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
            .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
            .AddInMemoryTokenCaches();

The last line (AddInMemoryTokenCaches) configure application to use in-memory cache that is empty after each server restart. We need to find a way to store tokens outside the app process and restore the cache after process restart.

Let’s take a look at supported caches out-of-the-box:

The only alternative to AddInMemoryTokenCaches is AddDistributedTokenCaches with ability to store tokens in memory, Redis, CosmosDB, SqlServer. Last three are nice options to distributed application but all of them are complicated for localhost development.

For our use case would be enough to serialise token cache to local file between restart. Luckily, it is not that complicated. We can implement IDistributedCache interface using TestDistributedCache as reference implementation.

public class LocalFileTokenCache :  IDistributedCache
{
    private class FileTransaction : IDisposable
    {
        public FileTransaction(string fileName = "cache.json")
        {
            var root = Path.GetDirectoryName(GetType().Assembly.Location);
            _fileName = Path.Combine(root, fileName);
            
            if (File.Exists(_fileName))
            {
                var str = File.ReadAllText(_fileName);
                Dict = JsonSerializer.Deserialize<Dictionary<string, byte[]>>(str);
            }
            
            Dict ??= new Dictionary<string, byte[]>();
        }

        private readonly string _fileName;
        public Dictionary<string, byte[]> Dict { get; }

        public void Dispose()
        {
            var str =JsonSerializer.Serialize(Dict);
            File.WriteAllText(_fileName, str);
        }
    }
    
    public byte[] Get(string key)
    {
        using var cache = new FileTransaction();
        return cache.Dict.TryGetValue(key, out var value) ? cache.Dict[key] : null;
    }

    public Task<byte[]> GetAsync(string key, CancellationToken token = default)
    {
        return Task.FromResult(Get(key));
    }

    public void Refresh(string key)
    {
        // Don't process anything
    }

    public Task RefreshAsync(string key, CancellationToken token = default)
    {
        Refresh(key);
        return Task.CompletedTask;
    }

    public void Remove(string key)
    {
        using var cache = new FileTransaction();
        cache.Dict.Remove(key, out _);
    }

    public Task RemoveAsync(string key, CancellationToken token = default)
    {
        Remove(key);
        return Task.CompletedTask;
    }

    public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
    {
        using var cache = new FileTransaction();
        cache.Dict[key] = value;
    }

    public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default)
    {
        Set(key, value, options);
        return Task.CompletedTask;
    }
}

LocalFileTokenCache implementation is not suitable for anything rather than local development.

The last step is to register LocalFileTokenCache in DI container as implementation of IDistributedCache instead of MemoryDistributedCache for development environment.

public class Startup
{
    public Startup(IConfiguration configuration, IWebHostEnvironment env)
    {
        Configuration = configuration;
        CurrentEnvironment = env;
    }

    public IConfiguration Configuration { get; }
    private IWebHostEnvironment CurrentEnvironment{ get; set; } 
    
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
            .EnableTokenAcquisitionToCallDownstreamApi(new[]
            {
                "User.Read", "Files.ReadWrite.AppFolder", "Files.ReadWrite"
            }).AddDistributedTokenCaches();

        if (CurrentEnvironment.IsDevelopment())
        {
            services.AddSingleton<IDistributedCache, LocalFileTokenCache>();
        }
        else
        {
            services.AddSingleton<IDistributedCache, MemoryDistributedCache>();
        }
        //...
    }
    //...
}

P.S. I hope that proper fix will land to official template.

Update 2021-04-06: There is an official guide how to setup a Redis cache in a Docker container for local testing, that also can be used to local development.

F# Weekly #14, 2021 – Free F# Deep Dives eBook

Welcome to F# Weekly,

A roundup of F# content from this past week:

News

Videos and Slides

Blogs

F# vNext

Highlighted projects

New Releases

That鈥檚 all for now. Have a great week.

If you want to help keep F# Weekly going, click here to jazz me with Coffee!

Buy Me A Coffeesubscribe_blog_button-02

 

F# Weekly #13, 2021 – F# Type checker

Welcome to F# Weekly,

A roundup of F# content from this past week:

News

Videos and Slides

Blogs

F# vNext

Highlighted projects

New Releases

That鈥檚 all for now. Have a great week.

If you want to help keep F# Weekly going, click here to jazz me with Coffee!

Buy Me A Coffeesubscribe_blog_button-02