• This repository has been archived on 30/Jun/2023
  • Stars
    star
    139
  • Rank 262,954 (Top 6 %)
  • Language
    C#
  • License
    MIT License
  • Created about 4 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A modern compile-time generated interception/proxy library

Icon Avatar

Avatar is a modern interception library which implements the proxy pattern and runs everywhere, even where run-time code generation (Reflection.Emit) is forbidden or limitted, like physical iOS devices and game consoles, through compile-time code generation. The proxy behavior is configured in code using what we call a behavior pipeline.

Avatars blend in with the Na'vi seamlessly, and you can control their behavior precisely by 'driving' them through a psionic link. Just like a proxy, with behavior driven through code.

Avatar Overloads

Version Downloads License Discord Chat GitHub

CI Version GH CI Status

NOTE: Avatar provides a fairly low-level API with just the essential building blocks on top of which higher-level APIs can be built, such as the upcoming Moq vNext API.

Requirements

Avatar is a .NET Standard 2.0 library and runs on any runtime that supports that.

Compile-time proxy generation leverages Roslyn source generators and therefore requires C# 9.0, which at this time is included in Visual Studio 16.8 (preview or later) and the .NET 5.0 SDK (RC or later). Compile-time generated proxies support the broadest possible run-time platforms since they don't require any Reflection.Emit, and also don't pay that performance cost either.

Whenever compile-time proxy generation is not available, a fallback generation strategy is used instead, which leverages Castle DynamicProxy to provide the run-time code generation.

The client API for configuring proxy behaviors in either case is exactly the same.

NOTE: even though generated proxies is the main usage for Avatar, the API was designed so that you can also consume the behavior pipeline easily from hand-coded proxies too.

Usage

ICalculator calc = Avatar.Of<ICalculator>();

calc.AddBehavior((invocation, next) => ...);

AddBehavior/InsertBehavior overloads allow granular control of the avatar's behavior pipeline, which is basically a chain of responsibility that invokes all configured behaviors that apply to the current invocation. Individual behaviors can determine whether to short-circuit the call or call the next behavior in the chain.

Avatar Overloads

Behaviors can also dynamically determine whether they apply to a given invocation by providing the optional appliesTo argument. In addition to the delegate-based overloads (called anonymous behaviors), you can also create behaviors by implementing the IAvatarBehavior interface:

public interface IAvatarBehavior
{
    bool AppliesTo(IMethodInvocation invocation);
    IMethodReturn Execute(IMethodInvocation invocation, ExecuteHandler next);
}

Common Behaviors

Some commonly used behaviors that are generally useful are provided in the library and can be added to avatars as needed:

  • DefaultValueBehavior: sets default values for method return and out arguments. In addition to the built-in supported default values, additional default value factories can be registered for any type.

  • DefaultEqualityBehavior: implements the Object.Equals and Object.GetHashCode members just like System.Object implements them.

  • RecordingBehavior: simple behavior that keeps track of all invocations, for troubleshooting or reporting.

Customizing Avatar Creation

If you want to centrally configure all your avatars, the easiest way is to simply provide your own factory method (i.e. Stub.Of<T>), which in turn calls the Avatar.Of<T> provided. For example:

    public static class Stub
    {
        [AvatarGenerator]
        public static T Of<T>() => Avatar.Of<T>()
            .AddBehavior(new RecordingBehavior())
            .AddBehavior(new DefaultEqualityBehavior())
            .AddBehavior(new DefaultValueBehavior());
    }

The [AvatarGenerator] attribute is required if you want to leverage the built-in compile-time code generation, since that signals to the source generator that calls to your API end up creating an avatar at run-time and therefore a generated type will be needed for it during compile-time. You can actually explore how this very same behavior is implemented in the built-in Avatar API which is provided as a content file:

avatar API source

The Avatar.cs contains, for example:

[AvatarGenerator]
public static T Of<T>(params object[] constructorArgs) => Create<T>(constructorArgs);

[AvatarGenerator]
public static T Of<T, T1>(params object[] constructorArgs) => Create<T>(constructorArgs, typeof(T1));

As you can see, the Avatar API itself uses the same extensibility mechanism that your own custom factory methods can use.

Static vs Dynamic Avatars

Depending on the project and platform, Avatars will automatically choose whether to use run-time proxies or compile-time ones (powered by Roslyn source generators). The latter are only supported when building C# 9.0+ projects.

You can opt out of the static avatars by setting EnableCompiledAvatars=false in your project file:

<PropertyGroup>
    <EnableCompiledAvatars>false</EnableCompiledAvatars>
</PropertyGroup>

This will switch the project to run-time proxies based on Castle.Core.

Debugging Optimizations

There is nothing more frustrating than a proxy you have carefully configured that doesn't behave the way you expect it to. In order to make this a less frustrating experience, avatars are carefully optimized for debugger display and inspection, so that it's clear what behaviors are configured, and invocations and results are displayed clearly and concisely. Here's the debugging display of the RecordingBehavior that just keeps track of invocations and their return values for example:

debugging display

And here's the invocation debugger display from an anonymous behavior:

behavior debugging

Samples

The samples folder in the repository contains a few interesting examples of how Avatar can be used to implement some fancy use cases. For example:

  • Forwarding calls to matching interface methods/properties (by signature) to a static class. The example uses this to wrap calls to System.Console via an IConsole interface.

  • Forwarding calls to a target object using the DLR (that backs the dynamic keyword in C#) API for high-performance late binding.

  • Custom Stub.Of<T> factory that creates avatars that have common behaviors configured automatically.

  • Custom avatar factory method that adds an int return value randomizer.

  • Configuring the built-in DefaultValueBehavior so that every time a string property is retrieved, it gets a random lorem ipsum value.

  • Logging all calls to an avatar to the Xunit output helper.

Sponsors

sponsorsΒ Β byΒ @clariusΒ sponsors

get mentioned here too!

More Repositories

1

moq

The most popular and friendly mocking framework for .NET
C#
5,709
star
2

GitInfo

Git and SemVer Info from MSBuild, C# and VB
Pascal
513
star
3

ThisAssembly

Exposes project and assembly level information as constants in the ThisAssembly class using source generators powered by Roslyn.
C#
418
star
4

SmallSharp

Create, edit and run multiple C# top-level programs in the same project by just selecting the startup program from the start button.
C#
279
star
5

nugetizer

A simple to understand packing model for authoring NuGet packages
C#
253
star
6

NuDoq

A standalone API to read .NET XML documentation files and optionally augment it with reflection information.
C#
105
star
7

dotnet-file

Download, update and sync loose files from URLs
C#
52
star
8

dotnet-vs

A global tool for managing Visual Studio installations
C#
49
star
9

DependencyInjection.Attributed

Provides compile-time discovery and code generation of service registrations from attributed types
C#
36
star
10

CloudActors

An opinionated, simplified and uniform Cloud Native actors' library that integrates with Microsoft Orleans.
C#
33
star
11

RxFree

An ultra-lightweight Rx source-only nuget to avoid depending on the full System.Reactive for IObservable<T> producers
C#
32
star
12

SponsorLink

SponsorLink: an attempt at OSS sustainability
C#
31
star
13

xunit.assemblyfixture

Provides per-assembly fixture state support for xunit
C#
24
star
14

WebSocketPipe

System.IO.Pipelines API adapter for System.Net.WebSockets
C#
24
star
15

Merq

Internal application architecture via command and event messages
C#
23
star
16

TableStorage

Repository pattern with POCO object support for storing to Azure / Cosmos DB Table Storage
C#
21
star
17

Injector

Allows injecting .NET code into another Windows process
C++
12
star
18

WebSocketeer

High-performance intuitive API for Azure Web PubSub protobuf subprotocol
C#
11
star
19

xunit.vsix

Xunit for creating VSIX integration tests
C#
10
star
20

dotnet-tor

A .NET cross-platform CLI app that uses TorSharp to run a local proxy
C#
10
star
21

oss

Basic repo configuration for my OSS projects
SCSS
10
star
22

azdo

Home for azdo.io and accompanying linkinator source
C#
9
star
23

Dynamically

Instantiate record types from dynamic data with compatible structural shapes, in-memory with no reflection or serialization, via compile-time source generators.
C#
9
star
24

Web

XLinq to Web
HTML
8
star
25

chromium

Run a portable Chromium using dotnet 6+ and nuget.
C#
8
star
26

WebSocketChannel

High-performance System.Threading.Channels API adapter for System.Net.WebSockets
C#
8
star
27

dotnet-evergreen

A dotnet tool runner that automatically updates the tool package before and while running it
C#
6
star
28

CredentialManager

Packages the Git Credential Manager cross-platform implementation for Windows, macOS and Linux for use as a generic credential manager.
C#
6
star
29

dotnet-gcm

A dotnet global tool to interface with the Git Credentials Manager Core
C#
5
star
30

dotnet-eventgrid

Azure Function app and accompanying dotnet global tool to monitor Azure EventGrid streams in real-time.
C#
5
star
31

System.Diagnostics.Tracer

An improved API on top of System.Diagnostics
C#
5
star
32

json

JsonPeek and JsonPoke tasks implementations
C#
5
star
33

dotnet-stop

Gracefully stops processes by sending them SIGINT (Ctrl+C) in a cross platform way.
C#
4
star
34

nosayudamos.org

C#
3
star
35

catbag

A repository of loose helpers, base clases and assorted code
C#
3
star
36

scraper

A web scraping app that runs on Azure Container Apps
C#
3
star
37

actions-bot

A GitHub Action that sets bot secrets as git defaults if present
3
star
38

tsh

C#
2
star
39

cloudy

Spike on cloud-first architecture for apps, heavily event-driven
C#
2
star
40

PackageReferenceCleaner

Clean your PackageReferences with PrivateAssets=all into beautiful one-liners, automatically
C#
2
star
41

shields

Custom endpoints for custom badges using https://shields.io/endpoint
C#
2
star
42

Cognitive

Unified Cognitive API for Azure, Amazon and Google
C#
2
star
43

html

Loads Html documents as XLinq XDocuments
HTML
2
star
44

epub

A lightweight library that implements EPUB standard
C#
2
star
45

sponsors

Automatically updated list of devlooped sponsors
1
star
46

yaml

YamlPeek MSBuild Task
C#
1
star
47

actions-includes

Processes include HTML comments in files and inserts the included files
PowerShell
1
star
48

jq

A nuget distribution of the official JQ implementation, for easy consumption from .NET
C#
1
star
49

Azure.Functions.OpenApi

A zero-code OpenAPI (Swagger) basic generator for Azure Functions
C#
1
star
50

Mvp.Xml

C#
1
star
51

StreamR

An opinionated streaming-based library for running assistants with support for tool/function invocation and assistant behavior composition.
C#
1
star