Skip to main content

AsyncIt by Oleg Shilo

NuGet / site data

Nuget GitHub last commit GitHub Repo stars

Details

Info

info

Name: AsyncIt

AsyncIt is a C# source generator (CodeAnalyzer) for automatic generation of async/sync versions of the type API.

Author: Oleg Shilo

NuGet: https://www.nuget.org/packages/AsyncIt/

You can find more details at https://github.com/oleg-shilo/AsyncIt/

Source: https://github.com/oleg-shilo/AsyncIt

Original Readme

note

AsyncIt

AsyncIt is a NuGet package library that allows the automatic generation of additional synchronous and asynchronous APIs for existing user codebase and external packages.

It aims to extend user-defined CLR types by automating the otherwise manual process of defining repetitive and straightforward routines. Thus, the development, maintenance and consumption of the released API are simplified due to the balanced (close to ideal) ratio of the synchronous and asynchronous API endpoints:

   Every functionality point has both Async and Sync API endpoints available.

This content is an extract from the project's main Wiki page. It is highly recommended that you read it, as it explains the deep reasons behind this project and details the more concrete usage scenarios.

Overview

AsyncIt is a source generator that is integrated into the .NET build process as a special tool type - the so-called "Analyzer". It is invoked by the compiler during the assembly build and allows the injection of missing API endpoints based on the present assembly API. Thus, if the assembly being built has the GetStatus but not the GetStatusAsync method, then AsyncIt will generate the missing method with a straightforward implementation. It can also generate the synchronous API if it is not present in the original codebase:

  • The API defines synchronous methods only:

    Original code

    public partial class DeviceLib
    {
    public static string GetStatus() {. . .}
    }

    Code that is fed to the C# compiler

    public partial class DeviceLib
    {
    public static string GetStatus() {. . .}
    }

    public partial class DeviceLib // AsyncIt generated
    {
    public static Task<string> GetStatusAsync()
    => TaskRun(() => GetStatus());
    }

AsyncIt does not do anything fancy. Like the await keyword, it cannot magically convert a synchronous routine into an asynchronous one and vice versa. Instead, it simply emits the code that the developer would type manually if he/she decides to use the API in a concurrency way that the API author did not anticipate.

AsyncIt can also be used to balance API of the external assemblies (e.g. .NET base classes, nuget packages)

This is where AsyncIt is placed in the overall .NET concurrency model architecture:

image

Usage

In order to integrate AsyncIt with your .NET project, add AsyncIt Nuget package.

dotnet add package AsyncIt

That's it. Now, you can mark any type for which you want to generate async/sync methods with the [Async] attribute (see the details below), and the new source code will be generated and included in the build.

You can always inspect the generated code in the Visual Studio solution explorer:

image

Extending user-defined types

In this scenario, a user type containing sync/async methods is extended by additional source file(s) implementing additional API methods. The type can be extended either with an additional partial class definition or by the extension methods class.

A typical usage can be illustrated by the code below.

Async scenario:

[Async]
public partial class BankService
{
public partial class OrderService
{
public Order GetOrder(int id) // and GetOrderAsync will be created by AsyncIt
{...}
}
}

...

async Task OnButtonClick(object sender, EventArgs args)
{
Order order = await service.GetOrderAsync(this.OrderId);
orderLabel.Text = order.Name;
}

Sync scenario:

[Async(Interface = Interface.Sync)]
partial class AccountService
{
public async Task<Account> GetAccountAsync(int id) // and GetAccount will be created by AsyncIt
{...}
}

...

static void Main()
{
var account = new AccountService().GetAccount(333);

File.WriteAllText($"account_{account.Id}.txt", account.Balance.ToString());
}

Extending external types

In this scenario, an external type (from a referenced assembly) containing sync/async methods is extended by additional source file(s) implementing additional API methods. The type can be extended by the extension methods class.

A typical usage can be illustrated by the code below for generating on-fly synchronous methods for type HttpClient .

Async scenario:

// For all synchronous methods of DirectoryInfo will be created an async equivalent by AsyncIt
[assembly: AsyncExternal(typeof(DirectoryInfo), Interface.Async)]

...

async Task OnButtonClick(object sender, EventArgs args)
{
var info = new DirectoryInfo(workingDir);

string[] folders = await info.GetDirectoriesAsync("*", SearchOption.AllDirectories);

foreach(var path in folders)
foldersListBox.Add(path);
}

Sync scenario:

// For all asynchronous methods of HttpClient will be created a sync equivalent by AsyncIt
[assembly: AsyncExternal(typeof(HttpClient), Interface.Sync)];

...

static void Main()
=> File.WriteAllText(
"temperature.txt",
new HttpClient().GetString("https://www.weather.com/au/melbourne/temperature"));

About

note

Generate async from sync or sync from async

How to use

Example (source csproj, source files)

This is the CSharp Project that references AsyncIt

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AsyncIt" Version="1.0.0-pre4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

Generated Files

Those are taken from $(BaseIntermediateOutputPath)\GX

// <auto-generated/>

using System;
using System.Reflection;

namespace AsyncIt
{

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class AsyncAttribute : Attribute
{
public AsyncAttribute()
{
}

public AsyncAttribute(Algorithm algorithm, Interface @interface)
{
Algorithm = algorithm;
Interface = @interface;
}
public AsyncAttribute(Interface @interface, Algorithm algorithm)
{
Algorithm = algorithm;
Interface = @interface;
}
public AsyncAttribute(Algorithm algorithm)
{
Algorithm = algorithm;
}

public AsyncAttribute(Interface @interface)
{
Interface = @interface;
}
public Algorithm Algorithm { get; set; }
public Interface Interface { get; set; }
internal string TypeGenericArgs;
internal string NamePattern;
}

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
public sealed class AsyncExternalAttribute : Attribute
{
public AsyncExternalAttribute()
{
}
public AsyncExternalAttribute(Type type)
{
Type = type;
}

public AsyncExternalAttribute(Type type, Interface @interface)
{
Type = type;
Interface = @interface;
}
public AsyncExternalAttribute(Type type, Interface @interface, string methods)
{
Type = type;
Interface = @interface;
Methods = methods;
}

public Interface Interface { get; set; }
public Type Type { get; set; }
public string Methods { get; set; } = "*";
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class IgnoreAttribute : Attribute
{
}

public enum Interface
{
Async,
Sync,
Full,
}

public enum Algorithm
{
PartialType,
ExtensionMethods
}
}

Useful

Download Example (.NET C#)

Share AsyncIt

https://ignatandrei.github.io/RSCG_Examples/v2/docs/AsyncIt

In the same category (Async) - 1 other generators

HsuSgSync