Implementing Missing Features in Entity Framework Core – Part 7: Entity Configuration in Mapping Classes

This is the seventh post in a series of posts about bringing the features that were present in Entity Framework pre-Core into EF Core. The others are:

  • Part 1: Introduction, Find, Getting an Entity’s Id Programmatically, Reload, Local, Evict

  • Part 2: Explicit Loading

  • Part 3: Validations

  • Part 4: Conventions

  • Part 5: Getting the SQL for a Query

  • Part 6: Lazy Loading

This time I’m going to cover automatic entity configuration in mapping classes, that is, outside of the DbContext.OnModelCreating method. If you remember, Entity Framework Code First supported having classes inheriting from EntityTypeConfiguration in the same assembly as the context, and these classes would be loaded automatically. This made it much simpler to add new mapping classes to a project without touching the context.

This functionality hasn’t been ported to Entity Framework Core yet, but it is being developed for the next version, and is tracked by ticket 2805. My implementation finds mapping classes in the same assembly as the context automatically, or it can load configuration explicitly from a mapping class. Here is my contract for the mapping class:

public interface IEntityTypeConfiguration<T> where T : class
{
void Configure(EntityTypeBuilder<T> entityTypeBuilder);
}

As you can see, this needs to be implemented in a concrete generic class and bound to a specific entity type.

The actual implementation goes like this:

public static class EntityTypeConfigurationExtensions
{
private static readonly MethodInfo entityMethod = typeof(ModelBuilder).GetTypeInfo().GetMethods().Single(x => (x.Name == "Entity") && (x.IsGenericMethod == true) && (x.GetParameters().Length == 0));

private static Type FindEntityType(Type type)
{
var interfaceType = type.GetInterfaces().First(x => (x.GetTypeInfo().IsGenericType == true) && (x.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)));
return interfaceType.GetGenericArguments().First();
}

private static readonly Dictionary<Assembly, IEnumerable<Type>> typesPerAssembly = new Dictionary<Assembly, IEnumerable<Type>>();

public static ModelBuilder ApplyConfiguration<T>(this ModelBuilder modelBuilder, IEntityTypeConfiguration<T> configuration) where T : class
{
var entityType = FindEntityType(configuration.GetType());

dynamic entityTypeBuilder = entityMethod
.MakeGenericMethod(entityType)
.Invoke(modelBuilder, new object[0]);

configuration.Configure(entityTypeBuilder);

return modelBuilder;
}

public static ModelBuilder UseEntityTypeConfiguration(this ModelBuilder modelBuilder)
{
IEnumerable<Type> configurationTypes;
var asm = Assembly.GetEntryAssembly();

if (typesPerAssembly.TryGetValue(asm, out configurationTypes) == false)
{
typesPerAssembly[asm] = configurationTypes = asm
.GetExportedTypes()
.Where(x => (x.GetTypeInfo().IsClass == true) && (x.GetTypeInfo().IsAbstract == false) && (x.GetInterfaces().Any(y => (y.GetTypeInfo().IsGenericType == true) && (y.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)))));
}

var configurations = configurationTypes.Select(x => Activator.CreateInstance(x));

foreach (dynamic configuration in configurations)
{
ApplyConfiguration(modelBuilder, configuration);
}

return modelBuilder;
}
}

You can see that it uses some dynamic magic to make things simpler, otherwise we’d need to have even more reflection. Dynamics take care of these things quite nicely.

The code essentially looks at the entry assembly and finds all non-abstract public types that implement IEntityTypeConfiguration<T>. For each of those, it creates an instance, extracts the template argument and creates an EntityTypeBuilder<T> from calling the Entity<T> method of the ModelBuilder class and calls the IEntityTypeConfiguration<T>.Configure method of the instantiated mapping class passing it the EntityTypeBuilder<T> which allows it to supply mapping configuration for the mapped entity (T).

We need to explicitly call this extension inside DbContext.OnModelCreating:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseEntityTypeConfiguration();
base.OnModelCreating(modelBuilder);
}

And it takes care of everything for us. Or, if we want to load a single mapping class explicitly, we can also do so:

modelBuilder.UseEntityTypeConfiguration<MyEntityTypeConfiguration>();

Finally, a simple mapping class might be:

public class MyEntityTypeConfiguration : IEntityTypeConfiguration<MyEntity>
{
public void Configure(EntityTypeBuilder<MyEntity> entityTypeBuilder)
{
entityTypeBuilder.ToTable("MyEntity");
entityTypeBuilder.Property(x => x.MyEntityId).HasColumnName("Id");
}
}

In case you’re interested, this feature is similar to the one being implemented for .NET Core, except that it doesn’t find mapping classes automatically. The IEntityTypeConfiguration<T> interface is exactly the same.

Technologies to Follow in 2017

Introduction

A lot is happening and it’s difficult to keep track of everything. Based on my work and on what I see over the Internet, I decided to write a post about the technologies – tools, languages, servers, operating systems, etc – that I find more interesting and promising. I know, some of these will be controversial, others are not exactly new, I am even mixing totally different things together, but, hey, it’s my opinion – feel free to share your objections here! Winking smile

I am not going to cover the myriad of JavaScript frameworks, because they’re just too many. I will only talk about what I know. For that same reason, I’m not talking about Akka, Go, Scala, Python, Ruby, Erlang, etc, because, honestly, I never used them. Also, I don’t cover Java, although Kafka is written in Java, because I haven’t used Java in anger for more than 10 years. I read that it’s having some problems, with some key people leaving Oracle, persisting security problems and the delaying of releases, but I’m sure Java is here to stay. Others will be more fit to talk about it.

.NET Core

.NET Core is Microsoft’s next version .NET framework, only this time totally modular, open source and multi-platform. Runs on Linux and MacOS, not just Windows. Still doesn’t have all the features of classic .NET, but it will get there: next version (2.0) will more than double the supported APIs. Right now, it’s perfect for writing .NET MVC apps and web APIs that need to run in other operating systems, including inside Docker containers. Get it from https://github.com/dotnet/core.

Node.js

A JavaScript runtime for the desktop instead of the browser. Uses an event-driven, asynchronous I/O model for high performance and scalability. Has probably the largest ecosystem of open source libraries in the world – NPM. Free and open source, with new features continuously being released. Currently uses Google’s V8 engine, but Microsoft submitted a patch to allow it to run Chakra, it’s JavaScript engine! Exciting times! Its site is https://nodejs.org.

Docker

You favorite container technology! Now supported in both Amazon Web Services and Azure, and with native support in Windows Server 2016. This is a must have for highly scalable applications. Free but it’s possible to get a paid repository online. A lot going on around it, the only problem is that things tend to change in non-retro-compatible ways, still need maturing. See more at http://docker.com.

Elasticsearch

A distributed and open source search engine based on Lucene. A blazing fast NoSQL database with replication capabilities, it is the most widely known component of the ELK stack, together with Kibana (for reporting and visualizations), Logstash (for data import) and Beats (for data shipping). Even Azure Search uses it behind the covers. Free but some tools are paid. Get it from http://elastic.co.

ECMAScript 2015

The next generation JavaScript, also known as ECMAScript 6. Heavily influenced by TypeScript, it offers a number of features from compiled languages, such as lambda functions, classes, type safety, etc. Before it’s available everywhere, people are using Babel.js to compile it to classic JavaScript. Google Chrome’s V8 engine already supports a great deal of it, as does Firefox. The specification is available here: http://www.ecma-international.org/ecma-262/6.0/.

HTML5

Is there any other, I hear you ask? Well, except if you need to support that old two-letter browser who had an infamous version 8, not really. Together with HTML 5 came a wealth of APIs that now allow us to have near-desktop quality apps on the web, and in some mobile browsers too. Latest standard is 5 but 5.1 is due to come out this year. Interestingly, HTML5 is more and more not just about web applications but also being used for desktop ones: for example, the Spotify desktop client is an HTML5 app. The specification is available at https://www.w3.org/TR/html5.

Kafka

A high throughput, low-latency open source message broker from the Apache foundation. Can stream data in real-time for massive simultaneous clients and has bindings for several languages. Similar to a distributed transaction log with exactly once semantics. More info here: https://kafka.apache.org.

TypeScript

A superset of JavaScript offering type safety and class-based object-oriented features. Nice wrappers around promises using similar syntax to C#’s async/await. It is compiled to JavaScript, so it can run anywhere JavaScript can. Free license. The official site is https://www.typescriptlang.org.

MongoDB

An open source NoSQL document database designed for high performance and with interesting clustering features. Mappings for all common languages, including scripting ones. With it you get JSON storage, indexes and automatic expiration. With free and commercial licenses. See more at http://www.mongodb.com.

Git

A free and open source distributed source control from the author of Linux. Now being used everywhere, even Microsoft is using it instead of their own TFS. Not an easy beast to master, I may add. Also worth mentioning GitFlow, a proposed workflow for branching and release management. The official site is https://git-scm.com.

Nginx

A high performance web server, HTTP cache and reverse proxy server for several TCP protocols. Can serve .NET and any other language, probably best used as a reverse proxy, particularly in the case of .NET Core. Runs on Windows and several UNIX flavors. It is free to use. Available from https://www.nginx.com.

Octopus Deploy

An automated deployment and release management tool for .NET applications. Latest versions can deploy both web apps as well as Windows services. Plays nicely with Continuous Integration and build tools such as Jenkins and TeamCity. Both free and commercial licenses. The web site is https://octopus.com.

Azure

Microsoft’s Cloud offering, the competitor of Amazon Web Services. Loaded with powerful services and features, which include amazing machine learning services, containerization as a service, queuing, and anything that can be expected from a Cloud service. Possible to get a time-limited trial for free. The official site is https://azure.microsoft.com.

Amazon Web Services

One of the two major players in the Cloud market, the other being Azure. Still has the biggest market share and offers a number of interesting features. Leaning slightly more towards Java and JavaScript than to .NET. Anyone can get a free account, as long as a credit card is supplied. See more at https://aws.amazon.com.

Linux

The free and open source operating system that just a few years ago Microsoft compared to a virus! Based on UNIX, now not just for geeks, it is everywhere, especially with the arriving of Docker. Several distributions available, to match anyone’s preferences, some free and some commercial. Windows 10 now even runs bash natively! See more at https://kernel.org.

Visual Studio Code

A powerful and extensible yet lightweight IDE from Microsoft based on GitHub’s Electron, which can run in a number of platforms, from Windows and Linux to MacOS. Includes support for a number of languages, Git integration, debugging capabilities – which make it stand from others such as Sublime or Atom – and an extension mechanism. Hey, it’s free! Get it from http://code.visualstudio.com.

Xamarin

A cross-platform implementation of .NET, for Windows Phone, Android and iOS. Before .NET Core came along – in fact, even after that – it is the preferred tool for creating applications that need to target multiple platforms. Now offered for free by Microsoft. but the Enterprise version will require a Visual Studio paid license. Microsoft promised to make it open source. Official site is https://www.xamarin.com.

Google Analytics

A web analytics service offered for free by Google, although paid subscriptions also exist. Can be used to track not only traffic but also custom events, and also in mobile apps. It’s unbelievable the amount of information that one can get out of it. See it in https://analytics.google.com.

SQL Server 2016

In-memory tables, JSON support, Query Store, integrated R, row and column-level security, etc, make this one of the most interesting versions of SQL Server ever. Available for free with limitations as Express edition, and as a paid license. More info from http://microsoft.com/sqlserver.

Let’s Encrypt

Free SSL certificates for the masses! No need to pay for a certificate, now you can get any number for free. Easily installable in any server (even IIS), but expires every 90 days. Get yours from https://letsencrypt.org.

TensorFlow

TensorFlow is Google’s second generation open source library for machine intelligence. It uses data flow graphs to represent mathematical operations and is the core of several Google products, such as Gmail, Google Photos and others. It offers Python and C++ bindings and recently it compiles on Windows as well as Linux and Mac OSX. Get it from https://github.com/tensorflow/tensorflow.

GitLab

GitLab is a free (with an enterprise license too) repository manager built on Git. It is fast moving with a plethora of very useful features. You can install it on premises or run it in the cloud. Offers integration with LDAP servers for authentication, offers a pretty decent Continuous Integration feature, plus a lot of other cool stuff. Check it out at https://about.gitlab.com.

Redis

A distributed cache with open source implementations in Linux and Windows. Currently, probably the most used one. Offered by both Azure and AWS. Not just BLOB cache, offers interesting structures. Learn about it at https://redis.io.

Conclusion

So, what are your thoughts – am I missing something? Do you agree or disagree with my choices? I’d love to hear from you!

Fusion Tech Talk #1

(Portuguese only, sorry!)

IMG_20170207_184521

Na passada terça-feira, 7, teve lugar nas instalações da Fusion Cowork o primeiro evento Fusion Tech Talk!

Tive a honra de fazer uma apresentação sobre a Microsoft e o open-source e a segunda apresentação, sobre Cake, foi feita pelo Pedro Marques (@pitermarx).

Tivemos uma boa afluência, cerca de 40 pessoas, o que, para um primeiro evento, não foi nada mau! Outro se seguirão, para tal, convido-vos a submeter ideias em https://www.meetup.com/Aveiro-Technology-Talk.

Obrigado ao Pedro Marques, à Fusion Cowork e a todos os que estiveram presentes, conto ver-vos nos próximos eventos! Winking smile


Implementing Missing Features in Entity Framework Core – Part 6: Lazy Loading

This will be the sixth post in my series of posts about bringing the features that were present in Entity Framework pre-Core into EF Core. The others are:

  • Part 1: Introduction, Find, Getting an Entity’s Id Programmatically, Reload, Local, Evict

  • Part 2: Explicit Loading

  • Part 3: Validations

  • Part 4: Conventions

  • Part 5: Getting the SQL for a Query

As you may know, the second major version of Entity Framework Core, 1.1, was released recently, however, some of the features that used to be in the non-Core versions still didn’t make it. One of these features is lazy loading of collections, and I set out to implement it… or, any way, something that I could use instead of it! Smile

Here’s what I came up with. First, let’s define a class that will act as a proxy to the collection to be loaded. I called it CollectionProxy<T>, and it goes like this:

internal sealed class CollectionProxy<T> : IList<T> where T : class
{
private bool _loaded;
private bool _loading;
private readonly DbContext _ctx;
private readonly string _collectionName;
private readonly object _parent;
private readonly List<T> _entries = new List<T>();

public CollectionProxy(DbContext ctx, object parent, string collectionName)
{
this._ctx = ctx;
this._parent = parent;
this._collectionName = collectionName;
}

private void EnsureLoaded()
{
if (this._loaded == false)
{
if (this._loading == true)
{
return;
}

this._loading = true;

var entries = this
._ctx
.Entry(this._parent)
.Collection(this._collectionName)
.Query()
.OfType<T>()
.ToList();

this._entries.Clear();

foreach (var entry in entries)
{
this._entries.Add(entry);
}

this._loaded = true;
this._loading = false;
}
}

IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
this.EnsureLoaded();

return this._entries.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return (this as ICollection<T>).GetEnumerator();
}

int ICollection<T>.Count
{
get
{
this.EnsureLoaded();
return this._entries.Count;
}
}

bool ICollection<T>.IsReadOnly
{
get
{
return false;
}
}

void ICollection<T>.Add(T item)
{
this.EnsureLoaded();
this._entries.Add(item);
}

void ICollection<T>.Clear()
{
this.EnsureLoaded();
this._entries.Clear();
}

bool ICollection<T>.Contains(T item)
{
this.EnsureLoaded();
return this._entries.Contains(item);
}

void ICollection<T>.CopyTo(T[] array, int arrayIndex)
{
this.EnsureLoaded();
this._entries.CopyTo(array, arrayIndex);
}

bool ICollection<T>.Remove(T item)
{
this.EnsureLoaded();
return this._entries.Remove(item);
}

T IList<T>.this[int index]
{
get
{
this.EnsureLoaded();
return this._entries[index];
}

set
{
this.EnsureLoaded();
this._entries[index] = value;
}
}

int IList<T>.IndexOf(T item)
{
this.EnsureLoaded();
return this._entries.IndexOf(item);
}

void IList<T>.Insert(int index, T item)
{
this.EnsureLoaded();
this._entries.Insert(index, item);
}

void IList<T>.RemoveAt(int index)
{
this.EnsureLoaded();
this._entries.RemoveAt(index);
}

public override string ToString()
{
this.EnsureLoaded();
return this._entries.ToString();
}

public override int GetHashCode()
{
this.EnsureLoaded();
return this._entries.GetHashCode();
}
}

You can see that, in order to be as compliant as possible, I made it implement IList<T>; this way, it can be easily compared and switched with, for example, ICollection<T> and, of course, the mother of all collections, IEnumerable<T>. How it works is simple:

  1. It receives in its constructor a pointer to a DbContext, the collection’s parent, and the collection-to-be-made-lazy’s name;
  2. There is an EnsureLoaded method that essentially checks if the collection has already been loaded, and, if not the case, does so, through the new (in EF Core 1.1) explicit loading API; it populates an internal list with the loaded collection’s items;
  3. Inner fields _loading and _loaded act as defenses to prevent the collection to be loaded twice, or to enter an infinite loop (stack overflow);
  4. I implemented all inherited methods and properties as explicit implementations, but there was no need for that, just a personal preference; all of them ensure that the collection is loaded (EnsureLoaded) before delegating to its internal field list;
  5. ToString and GetHashCode delegate to the internal list as well.

I created as well an extension method to make it’s usage more simple:

public static class CollectionExtensions
{
public static void Wrap<TParent, TChild>(this DbContext ctx, TParent parent, Expression<Func<TParent, IEnumerable<TChild>>> collection) where TParent : class where TChild : class
{
var prop = ((collection.Body as MemberExpression).Member as PropertyInfo);
var propertyName = prop.Name;

prop.SetValue(parent, new CollectionProxy<TChild>(ctx, parent, propertyName));
}
}

As you can see, I kept it very simple – no null/type checking or whatever, that is left to you, dear reader, as an exercise! Winking smile

Finally, here’s how to use it:

using (var ctx = new MyContext())
{
var parentEntity = ctx.MyParentEntities.First();

ctx.Wrap(parentEntity, x => x.MyChildren); //sets up the proxy collection

var childEntitiesCount = parentEntity.MyChildren.Count(); //forces loading

foreach (var child in parentEntity.MyChildren) //already loaded, so iterate in memory
{
child.ToString();
}
}

Hope you like it! Let me know your thoughts!

What’s New in Entity Framework Core 1.1

Introduction

Entity Framework Core 1.1 was released last November. With it, besides some bug fxes and semi-transparent improvements, came along a few goodies. If you read my previous post on features missing in Entity Framework Core 1.0, you’ll be please to know that a few have been addressed.

New API Methods

The Find method, for returning an entity from its primary key, is back (I had provided a workaround here):

var e1 = ctx.DbSet<MyEntity>().Find(1);

New is GetDatabaseValues, which goes to the database and fetches the current values for the current entity and primary key:

var dbProperties = ctx.Entry<MyEntity>(e).GetDatabaseValues();

Reload and Explicit Load

It is now again possible to reload an entity, causing it to be re-hydrated with the current values from the database, through the Reload method (also available as a workaround here):

ctx.Entry<MyEntity>(e).Reload();

And it is also possible to force load a not-loaded collection ():

ctx.Entry<MyEntity>(e).Collection(x => x.MyColl).Load();

As well as entity references (one-to-one, many-to-one):

ctx.Entry<MyEntity>(e).Reference(x => x.MyRef).Load();

Connection Resiliency

Connection resiliency made it way to version 1.1 as well:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer("my connection string", opt => opt.EnableRetryOnFailure());

base.OnConfiguring(optionsBuilder);
}

The EnableRetryOnFailure method is just a wrapper around ExecutionStrategy passing it SqlServerRetryingExecutionStrategy:

optionsBuilder
.ExecutionStrategy(x => new MyExecutionStrategy(x));

This one allows you to provide your own strategy for retries, by implementing IExecutionStrategy.

Configurable Change Tracking

Now, this is something that could have been really cool, but, as it is now, I find it somewhat crippled… you can now tell Entity Framework Core how should it find out if an entity has changed – the common change tracker functionality. But, the only supported techniques are the built-in (default, based on snapshots) or the use of INotifyPropertyChanged/INotifyCollectionChanged. This is not really that extensible, as you only have these two options. Here is how you configure it:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>()
.HasChangeTrackingStrategy(ChangeTrackingStrategy.ChangedNotifications);

base.OnModelCreating(modelBuilder);
}

If you want to use this approach, your entity must implement INotifyPropertyChanged and all of its collections must implement INotifyCollectionChanged. If any of the properties or collections in it changes, you must raise the PropertyChanged or CollectionChanged events, otherwise EF will not know that it is modified.

This can be set as the default for all entities, by the way:

modelBuilder
.HasChangeTrackingStrategy(ChangeTrackingStrategy.ChangedNotifications);

Using Fields

A most welcome addition, that was never previously available, is mapping to fields! This better supports a pure Domain Driven Design approach. It needs to be configured using code mapping:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<MyEntity>()
.Property(b => b.MyProp)
.HasField("_myField");

base.OnModelCreating(modelBuilder);
}

IEnumerable Collections

Another handy improvement is the ability to map collections declared as IEnumerable<T>, whereas in the past this was only possible for ICollection<T> (and derived classes, of course). The configuration is the same:

modelBuilder
.Entity<MyEntity>()
.HasMany(x => x.Children);

Of course, the concrete collection class must itself implement ICollection<T>, otherwise Entity Framework would have no way to populate it:

public class MyEntity
{
public IEnumerable<MyChild> Children { get; } = new HashSet<MyChild>();
}

Support for SQL Server In Memory Tables

In case you are using Hekaton, you can now tell Entity Framework that your entity is persisted as a memory-optimized table:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>()
.ForSqlServerIsMemoryOptimized();

base.OnModelCreating(modelBuilder);
}

Switching Services

Last, but not least, EF Core 1.1 makes it much easier to replace one of the services that EF uses internally:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.ReplaceService<IEntityStateListener, CustomEntityStateListener>();

base.OnConfiguring(optionsBuilder);
}

Conclusion

It’s nice to see things progressing, but there’s still a long way to go. In particular, as I said in my post about the missing features, there are quite a few features that still didn’t make it. In particular, I still miss:

  • Group By translation
  • Lazy loading
  • Date/Time operations
  • Support for custom SQL functions
  • Many-to-many relations
  • Command and query interception

We just have to wait for the next priorities of the team.

MVP Award Renewed

It was with great pleasure that I read Microsoft’s email today announcing me that I had renewed as MVP for the third year!

image

Being an MVP is both an honor and a privilege, and I feel both proud and humble to again be a part of this fantastic group of people! Thanks, Cristina, for this!

I also want to congratulate all of the other Portuguese MVPs that were renewed this year, and leave a word of comfort to those who haven’t. All the best, guys (and lady)! Winking smile

2016 in Review

Another year is over, time to review what I did this time… so here it goes:

Some trends:

Not all was good, though… was unable to finish on time an article for a development magazine… and was late on delivering another ebook… and couldn’t start another book, due to pure lack of time… more news on the first two next year, hopefully!

Next year I imagine I will be writing more and more on .NET Core and specifically ASP.NET Core. Probably some Elasticsearch and Docker (new!) too. Some “classic” topics will have more contents soon:

Azure and AWS will probably pop up too. Too soon to tell!

Anyway, I wish you all an excellent 2017! Thanks for visiting me, keep dropping by! Winking smile

Entity Framework Core Cookbook – Second Edition

Some of you may be aware that my new book for Packt Publishing is out! It is titled Entity Framework Core Cookbook – Second Edition because it was meant to be the second edition of Entity Framework 4.1: Expert’s Cookbook. In fact, it is mostly a full rewrite.

It is organized in chapters:

Chapter 1: Improving Entity Framework in the Real World

Chapter 2: Mapping Entities

Chapter 3: Validation and Changes

Chapter 4: Transactions and Concurrency Control

Chapter 5: Querying

Chapter 6: Advanced Scenarios

Chapter 7: Performance and Scalability

Appendix: Pitfalls

When I started writing it, .NET Core was still in early RC1. Things changed a lot from RC1 to RC2 and then again to RTM, so I had to revisit all chapters in the end. It was a pity that EF Core 1.1 was released shortly after the book was closed, because I could have talked about it too. Also, there are things that I could have covered, like extending Entity Framework Core, but there were so many of them! Smile Maybe in a future time!

Those of you who are interested can get a copy from the Pack Publishing site or from other sellers, either as an e-book or in hardcopy.

I want to thank everyone at Packt Publishing, namely Chaitanya Nair, Merint Mathew and Siddhi Chavan for their professionalism and support!

What’s New in .NET Core 1.1

.NET Core 1.1 – including ASP.NET Core and Entity Framework Core – was just released at the time of the Connect(); event. With it came some interesting features and improvements.

Before you start using version 1.1 you need to make sure you install the .NET Core 1.1 SDK from https://www.microsoft.com/net/download/core. If you don’t, some stuff will not work properly.

Here are some highlights.

ASP.NET Core

In version 1.1 you can now treat View Components like Tag Helpers! Not sure why they did this, but I guess it’s OK.

You new have URL rewriting middleware that can consume the same configuration file as the IIS URL Rewrite Module.

Also new is Caching middleware, bringing what was Output Cache in ASP.NET Web Forms to Core Land.

GZip compression is also starring as a middleware component.

Middleware components can now be applied as global attributes. Seems interesting, but I don’t know how this works, because we can’t specify the ordering.

Next big thing is WebListener. It’s another HTTP server, but this time tuned for Windows. Because it this, it supports Windows authentication, port sharing, HTTPS with Server Name Indication (SNI), HTTP/2 over TLS (on Windows 10), direct file transmission, and response caching WebSockets (on Windows 8 or higher).

Temp data can now be stored in a cookie, as with MVC pre-Core.

You can now log to Azure App Service and you can also get configuration information from Azure Key Vault. Still on Azure, you can make use of Redis and Azure Storage Data Protection.

Finally, something that was also previously available is view precompilation. Now you can build your views at compile time and get all errors ahead of time.

Not all is here, though: for example, mobile views are still not available.

More on https://blogs.msdn.microsoft.com/webdev/2016/11/16/announcing-asp-net-core-1-1 and https://github.com/aspnet/home/releases/1.1.0.

Entity Framework Core

The Find method is back, allowing us to load entities by their primary keys. As a side note, I have published a workaround for this for the initial version of EF Core 1.0. Same for Reload, GetModifiedProperties and GetDatabaseValues.

Explicit loading for references and collections is also here.

Connection resiliency, aka, the ability to retry a connection, also made its move to version 1.1, similar to what it was in pre-Core.

Totally new is the support for SQL Server’s Memory Optimized Tables (Hekaton).

Now we can map to fields, not just properties! This was an often requested feature, which helps follow a Domain Driven Design approach.

Also new is the capacity to change a specific service implementation without changing the whole service provider at startup.

There are more API changes and apparently LINQ translation has improved substantially. Time will tell!

A lot is still missing from pre-Core, see some of it here.

More info here: https://blogs.msdn.microsoft.com/dotnet/2016/11/16/announcing-entity-framework-core-1-1 and here: https://github.com/aspnet/EntityFramework/releases/tag/rel%2F1.1.0.

.NET Core

First of all, .NET Core 1.1 can now be installed in more Linux distributions than before and also in MacOS 10 and in Windows Server 2016.

The dotnet CLI has a new template for .NET Core projects.

.NET Core 1.1 supports .NET Standard 1.6.

Lots of performance improvements, bug fixes and imported APIs from .NET full.

Read more about it here: https://blogs.msdn.microsoft.com/dotnet/2016/11/16/announcing-net-core-1-1/ and here: https://github.com/dotnet/core/tree/master/release-notes/1.1.

Interception in .NET – Part 4: An Interception Framework

Introduction

This is the fourth and possibly final post on my interception in .NET series. See the last one here. This time, I’m going to present a framework for doing dynamic interception. Mind you, this is proof of concept code, it is not ready for production usage, although it can do some interesting things! This is going to be a long post, bear with me!

Core Concepts and Types

As I mentioned on the second post, there are essentially four dynamic interception techniques for .NET:

  • Virtual method interception
  • Interface interception
  • Transparent proxy interception
  • Context-bound object interception

I’m going to introduce another one, interception using dynamics, at the end of this post, I think it has some merits of its own.

The interceptors are organized into two groups:

  • Instance interceptors: interface interceptor, dynamic interceptor, context-bound object interceptor, transparent proxy interceptor
  • Type interceptors: virtual method interceptor

An instance interceptor is one that takes an existing object and intercepts calls made to its methods. A type interceptor, on the other hand, generates a type deriving from a given one that, when instantiated, will have the interception hooks that we chose to add to it.

The interception hooks mean that we can intercept a method call – and remember that properties are actually methods – and execute custom code before, after or instead of the intercepted method. An interception hook for an instance interceptor is an instance of a class that implements some interface, and for a type interceptor, it is a type that also implements that specific interface.

Let’s see some basic interfaces, first, the IInterceptor:

public interface IInterceptor
{
}

Not much to it, as you can see, I only use it as the base for both the instance and type interceptor contracts. Next, the IInstanceInterceptor, that is, the contract for an instance interceptor:

public interface IInstanceInterceptor : IInterceptor
{
object Intercept(object instance, Type typeToIntercept, IInterceptionHandler handler);

bool CanIntercept(object instance);
}

Only two methods, nothing too complex:

  • CanIntercept returns true if the instance can be intercepted, by the particular interceptor
  • Intercept receives the instance to intercept, an optional interface type and returns a proxy to the object being intercepted

I would also like to introduce now the IInterceptionHandler interface, which represents our interception hook:

public interface IInterceptionHandler
{
void Invoke(InterceptionArgs arg);
}

Only one method, Invoke, which is called when an interception is under way. Its InterceptionArgs contains the method to be intercepted, the actual instance where the method is being invoked, any method arguments and the desired return value:

[Serializable]
public sealed class InterceptionArgs : EventArgs
{
private object result;

public InterceptionArgs(object instance, MethodInfo method, params object [] arguments)
{
this.Instance = instance;
this.Method = method;
this.Arguments = arguments;
}

public void Proceed()
{
this.Result = this.Method.Invoke(this.Instance, this.Arguments);
}

public object Instance
{
get;
private set;
}

public MethodInfo Method
{
get;
private set;
}

public object [] Arguments
{
get;
private set;
}

public bool Handled
{
get;
set;
}

public object Result
{
get
{
return (this.result);
}
set
{
this.result = value;
this.Handled = true;
}
}
}

Noteworthy:

  • Method is, of course, the method being intercepted, which may well be either the setter or the getter of a property
  • Result will contain the result of the execution, if the intercepted method is not void
  • Arguments are the arguments being passed to the intercepted method
  • Instance is the actual instance on which the method was called
  • Handled is a flag that is set by the IInterceptionHandler’s Invoke method to tell the framework that we intercepted the normal course of the call
  • The Proceed method, if called, will cause the intercepted call to execute as if it hadn’t been intercepted

The ITypeInterceptor type, the contract for type interceptors:

public interface ITypeInterceptor : IInterceptor
{
Type Intercept(Type typeToIntercept, Type interceptionType);

bool CanIntercept(Type typeToIntercept);
}

Pretty similar to IInstanceInterceptor, but:

  • CanIntercept takes a Type instead of an instance
  • Intercept also takes the Type to intercept and the interception type (implementing IInterceptionHandler) and returns another Type, which should be a subclass of the Type to intercept

And, finally, the interface that is going to be implemented by generated (proxified) types, IInterceptionProxy:

public interface IInterceptionProxy
{
IInterceptor Interceptor
{
get;
}
}

Now let’s see how we implement the actual interceptors.

Virtual Method Interceptor

A virtual method interceptor is a type interceptor, it needs to generate dynamically a class that inherits from the passed type and, for all of its virtual methods that are marked for interception, override them in this dynamic class, adding hooks for the interception aspect.

There are basically two APIs in classic .NET for generating code at runtime:

Recently, we have another option: Roslyn. But I won’t talk about it here, definitely a topic for a future series of posts on its own.

For this example, I am going to use CodeDOM. The VirtualMethodInterceptor class looks like this:

public sealed class VirtualMethodInterceptor : ITypeInterceptor
{
private readonly InterceptedTypeGenerator generator;

public VirtualMethodInterceptor(InterceptedTypeGenerator generator)
{
this.generator = generator;
}

public VirtualMethodInterceptor() : this(CodeDOMInterceptedTypeGenerator.Instance)
{

}

private Type CreateType(Type typeToIntercept, Type handlerType)
{
return (this.generator.Generate(this, typeToIntercept, handlerType));
}

public Type Intercept(Type typeToIntercept, Type handlerType)
{
if (typeToIntercept == null)
{
throw (new ArgumentNullException("typeToIntercept"));
}

if (handlerType == null)
{
throw (new ArgumentNullException("handlerType"));
}

if (this.CanIntercept(typeToIntercept) == false)
{
throw (new ArgumentException("typeToIntercept"));
}

if (typeof(IInterceptionHandler).IsAssignableFrom(handlerType) == false)
{
throw (new ArgumentException("handlerType"));
}

if (handlerType.IsPublic == false)
{
throw (new ArgumentException("handlerType"));
}

if ((handlerType.IsAbstract == true) || (handlerType.IsInterface == true))
{
throw (new ArgumentException("handlerType"));
}

if (handlerType.GetConstructor(Type.EmptyTypes) == null)
{
throw (new ArgumentException("handlerType"));
}

return (this.CreateType(typeToIntercept, handlerType));
}

public bool CanIntercept(Type typeToIntercept)
{
return ((typeToIntercept.IsInterface == false) && (typeToIntercept.IsSealed == false));
}
}

The biggest part of the work is done, however, by a type generator. We have a base class for it, InterceptedTypeGenerator:

public abstract class InterceptedTypeGenerator
{
public abstract Type Generate(IInterceptor interceptor, Type baseType, Type handlerType, params Type [] additionalInterfaceTypes);
}

Its contract specifies the base type (that is going to be subclassed), any additional interfaces and an interception handler type, which needs to implement IInterceptionHandler and have a public parameterless constructor.

And here is a concrete implementation using CodeDOM, CodeDOMInterceptedTypeGenerator:

internal class CodeDOMInterceptedTypeGenerator : InterceptedTypeGenerator
{
public static readonly InterceptedTypeGenerator Instance = new CodeDOMInterceptedTypeGenerator();

private static readonly CodeDomProvider provider = new CSharpCodeProvider();
private static readonly CodeGeneratorOptions options = new CodeGeneratorOptions() { BracingStyle = "C" };
private static readonly CodeTypeReference proxyTypeReference = new CodeTypeReference(typeof(IInterceptionProxy));
private static readonly CodeTypeReference interceptorTypeReference = new CodeTypeReference(typeof(IInterceptor));
private static readonly CodeTypeReference handlerTypeReference = new CodeTypeReference(typeof(IInterceptionHandler));
private static readonly Assembly proxyAssembly = typeof(IInterceptionProxy).Assembly;
private static readonly Type interfaceProxyType = typeof(InterfaceProxy);

protected virtual void GenerateConstructors(CodeTypeDeclaration targetClass, Type baseType, IEnumerable<ConstructorInfo> constructors)
{
foreach (var constructor in constructors)
{
var c = new CodeConstructor();
targetClass.Members.Add(c);

c.Attributes = MemberAttributes.Final | MemberAttributes.Override;

foreach (var parameter in constructor.GetParameters())
{
c.Parameters.Add(new CodeParameterDeclarationExpression(parameter.ParameterType, parameter.Name));
}

if (baseType == interfaceProxyType)
{
c.Attributes |= MemberAttributes.Public;
}
else
{
if ((constructor.Attributes & MethodAttributes.Public) == MethodAttributes.Public)
{
c.Attributes |= MemberAttributes.Public;
}
else if ((constructor.Attributes & MethodAttributes.FamORAssem) == MethodAttributes.FamORAssem)
{
c.Attributes |= MemberAttributes.FamilyOrAssembly;
}
else if ((constructor.Attributes & MethodAttributes.Family) == MethodAttributes.Family)
{
c.Attributes |= MemberAttributes.Family;
}
else if ((constructor.Attributes & MethodAttributes.FamANDAssem) == MethodAttributes.FamANDAssem)
{
c.Attributes |= MemberAttributes.FamilyAndAssembly;
}
}

foreach (var p in constructor.GetParameters())
{
c.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression(p.Name));
}
}
}

protected virtual void GenerateMethods(CodeTypeDeclaration targetClass, Type baseType, IEnumerable<MethodInfo> methods)
{
if (methods.Any() == true)
{
var finalizeMethod = methods.First().DeclaringType.GetMethod("Finalize", BindingFlags.NonPublic | BindingFlags.Instance);

foreach (var method in methods)
{
if (method == finalizeMethod)
{
continue;
}

if (method.IsSpecialName == true)
{
continue;
}

var m = new CodeMemberMethod();
m.Name = method.Name;
m.ReturnType = new CodeTypeReference(method.ReturnType);

if (baseType != interfaceProxyType)
{
m.Attributes = MemberAttributes.Override;
}

targetClass.Members.Add(m);

foreach (var parameter in method.GetParameters())
{
m.Parameters.Add(new CodeParameterDeclarationExpression(parameter.ParameterType, parameter.Name));
}

if ((method.Attributes & MethodAttributes.Public) == MethodAttributes.Public)
{
m.Attributes |= MemberAttributes.Public;
}
else if ((method.Attributes & MethodAttributes.FamORAssem) == MethodAttributes.FamORAssem)
{
m.Attributes |= MemberAttributes.FamilyOrAssembly;
}
else if ((method.Attributes & MethodAttributes.Family) == MethodAttributes.Family)
{
m.Attributes |= MemberAttributes.Family;
}
else if ((method.Attributes & MethodAttributes.FamANDAssem) == MethodAttributes.FamANDAssem)
{
m.Attributes |= MemberAttributes.FamilyAndAssembly;
}

if (baseType == interfaceProxyType)
{
m.Attributes = MemberAttributes.Public | MemberAttributes.Final;
}

var currentMethod = new CodeVariableDeclarationStatement(typeof(MethodInfo), "currentMethod", new CodeCastExpression(typeof(MethodInfo), new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(MethodBase)), "GetCurrentMethod"))));
var getType = new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "instance"), "GetType");
var getMethod = new CodeMethodInvokeExpression(getType, "GetMethod", new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("currentMethod"), "Name"));
var originalMethod = new CodeVariableDeclarationStatement(typeof(MethodInfo), "originalMethod", getMethod);

m.Statements.Add(currentMethod);
m.Statements.Add(originalMethod);

var ps = new CodeExpression[] { new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "instance"), new CodeVariableReferenceExpression("originalMethod") }.Concat(method.GetParameters().Select(x => new CodeVariableReferenceExpression(x.Name))).ToArray();
var arg = new CodeVariableDeclarationStatement(typeof(InterceptionArgs), "args", new CodeObjectCreateExpression(typeof(InterceptionArgs), ps));

m.Statements.Add(arg);

var handler = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "handler"), "Invoke"), new CodeVariableReferenceExpression("args"));
m.Statements.Add(handler);

var comparison = new CodeBinaryOperatorExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("args"), "Handled"), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(method.ReturnType != typeof(void)));
CodeConditionStatement @if = null;

if (method.ReturnType != typeof(void))
{
if (baseType != interfaceProxyType)
{
@if = new CodeConditionStatement(comparison, new CodeMethodReturnStatement(new CodeCastExpression(method.ReturnType, new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("args"), "Result"))));

m.Statements.Add(@if);
m.Statements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), method.Name, method.GetParameters().Select(x => new CodeArgumentReferenceExpression(x.Name)).ToArray())));
}
else
{
@if = new CodeConditionStatement(comparison, new CodeMethodReturnStatement(new CodeCastExpression(method.ReturnType, new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("args"), "Result"))));

m.Statements.Add(@if);
m.Statements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeCastExpression(method.DeclaringType, new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "instance")), m.Name), method.GetParameters().Select(x => new CodeArgumentReferenceExpression(x.Name)).ToArray())));
}
}
else
{
if (baseType != interfaceProxyType)
{
@if = new CodeConditionStatement(comparison, new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), method.Name, method.GetParameters().Select(x => new CodeArgumentReferenceExpression(x.Name)).ToArray())));

m.Statements.Add(@if);
}
else
{
@if = new CodeConditionStatement(comparison, new CodeMethodReturnStatement(new CodeCastExpression(method.ReturnType, new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("args"), "Result"))));

m.Statements.Add(@if);
m.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeCastExpression(method.DeclaringType, new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "instance")), m.Name), method.GetParameters().Select(x => new CodeArgumentReferenceExpression(x.Name)).ToArray())));
}
}
}
}
}

protected virtual void GenerateProperties(CodeTypeDeclaration targetClass, Type baseType, IEnumerable<PropertyInfo> properties)
{
var interceptorProperty = new CodeMemberProperty();
interceptorProperty.Name = "Interceptor";
interceptorProperty.Type = interceptorTypeReference;
interceptorProperty.Attributes |= MemberAttributes.Private;
interceptorProperty.PrivateImplementationType = proxyTypeReference;
interceptorProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "interceptor")));
targetClass.Members.Add(interceptorProperty);

foreach (var property in properties)
{
var p = new CodeMemberProperty();
p.Name = property.Name;
p.Type = new CodeTypeReference(property.PropertyType);

if (baseType != interfaceProxyType)
{
p.Attributes = MemberAttributes.Override;
}

targetClass.Members.Add(p);

if (property.CanRead == true)
{
p.HasGet = true;

if ((property.GetMethod.Attributes & MethodAttributes.Public) == MethodAttributes.Public)
{
p.Attributes |= MemberAttributes.Public;
}
else if ((property.GetMethod.Attributes & MethodAttributes.FamORAssem) == MethodAttributes.FamORAssem)
{
p.Attributes |= MemberAttributes.FamilyOrAssembly;
}
else if ((property.GetMethod.Attributes & MethodAttributes.Family) == MethodAttributes.Family)
{
p.Attributes |= MemberAttributes.Family;
}
else if ((property.GetMethod.Attributes & MethodAttributes.FamANDAssem) == MethodAttributes.FamANDAssem)
{
p.Attributes |= MemberAttributes.FamilyAndAssembly;
}

if (baseType == interfaceProxyType)
{
p.Attributes = MemberAttributes.Public | MemberAttributes.Final;
}

var currentMethod = new CodeVariableDeclarationStatement(typeof(MethodInfo), "currentMethod", new CodeCastExpression(typeof(MethodInfo), new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(MethodBase)), "GetCurrentMethod"))));
var getType = new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "instance"), "GetType");
var getMethod = new CodeMethodInvokeExpression(getType, "GetMethod", new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("currentMethod"), "Name"));
var originalMethod = new CodeVariableDeclarationStatement(typeof(MethodInfo), "originalMethod", getMethod);

p.GetStatements.Add(currentMethod);
p.GetStatements.Add(originalMethod);

var ps = new CodeExpression[] { new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "instance"), new CodeVariableReferenceExpression("originalMethod") };
var arg = new CodeVariableDeclarationStatement(typeof(InterceptionArgs), "args", new CodeObjectCreateExpression(typeof(InterceptionArgs), ps));

p.GetStatements.Add(arg);

var handler = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "handler"), "Invoke"), new CodeVariableReferenceExpression("args"));
p.GetStatements.Add(handler);

var comparison = new CodeBinaryOperatorExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("args"), "Handled"), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(true));
var @if = new CodeConditionStatement(comparison, new CodeMethodReturnStatement(new CodeCastExpression(property.PropertyType, new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("args"), "Result"))));

p.GetStatements.Add(@if);

if (baseType != interfaceProxyType)
{
p.GetStatements.Add(new CodeMethodReturnStatement(new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), property.Name)));
}
else
{
p.GetStatements.Add(new CodeMethodReturnStatement(new CodePropertyReferenceExpression(new CodeCastExpression(property.DeclaringType, new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "instance")), property.Name)));
}
}

if (property.CanWrite == true)
{
p.HasSet = true;

var ps = new CodeExpression[]
{
new CodeThisReferenceExpression(),
new CodeCastExpression(typeof(MethodInfo), new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(MethodBase)), "GetCurrentMethod"))),
new CodeVariableReferenceExpression("value")
};

var arg = new CodeVariableDeclarationStatement(typeof(InterceptionArgs), "args", new CodeObjectCreateExpression(typeof(InterceptionArgs), ps));

p.SetStatements.Add(arg);

var handler = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "handler"), "Invoke"), new CodeVariableReferenceExpression("args"));
p.SetStatements.Add(handler);

var comparison = new CodeBinaryOperatorExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("args"), "Handled"), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(true));
var @if = new CodeConditionStatement(comparison, new CodeMethodReturnStatement());

p.SetStatements.Add(@if);

if (baseType != interfaceProxyType)
{
p.SetStatements.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), property.Name), new CodeVariableReferenceExpression("value")));
}
else
{
p.SetStatements.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeCastExpression(property.DeclaringType, new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "instance")), property.Name), new CodeVariableReferenceExpression("value")));
}
}
}
}

protected virtual void GenerateFields(CodeTypeDeclaration targetClass, Type baseType, Type handlerType, Type interceptorType)
{
if (handlerType != null)
{
var handlerField = new CodeMemberField();
handlerField.Attributes = MemberAttributes.FamilyOrAssembly;
handlerField.Name = "handler";
handlerField.Type = handlerTypeReference;
handlerField.InitExpression = (handlerType != null) ? new CodeObjectCreateExpression(handlerType) : null;
targetClass.Members.Add(handlerField);
}

var interceptorField = new CodeMemberField();
interceptorField.Attributes = MemberAttributes.FamilyOrAssembly;
interceptorField.Name = "interceptor";
interceptorField.Type = interceptorTypeReference;
interceptorField.InitExpression = (interceptorType != null) ? new CodeObjectCreateExpression(interceptorType) : null;
targetClass.Members.Add(interceptorField);
}

protected virtual void AddReferences(CompilerParameters parameters, Type baseType, Type handlerType, Type interceptorType, Type [] additionalInterfaceTypes)
{
parameters.ReferencedAssemblies.Add(string.Concat(proxyAssembly.GetName().Name, Path.GetExtension(proxyAssembly.CodeBase)));
parameters.ReferencedAssemblies.Add(string.Concat(baseType.Assembly.GetName().Name, Path.GetExtension(baseType.Assembly.CodeBase)));

if (handlerType != null)
{
parameters.ReferencedAssemblies.Add(string.Concat(handlerType.Assembly.GetName().Name, Path.GetExtension(handlerType.Assembly.CodeBase)));
}

if (interceptorType != null)
{
parameters.ReferencedAssemblies.Add(string.Concat(interceptorType.Assembly.GetName().Name, Path.GetExtension(interceptorType.Assembly.CodeBase)));
}

foreach (var additionalInterfaceType in additionalInterfaceTypes)
{
parameters.ReferencedAssemblies.Add(string.Concat(additionalInterfaceType.Assembly.GetName().Name, Path.GetExtension(additionalInterfaceType.Assembly.CodeBase)));
}
}

protected virtual IEnumerable<PropertyInfo> GetProperties(Type baseType, Type[] additionalInterfaceTypes)
{
if (baseType != interfaceProxyType)
{
return (baseType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => ((x.CanRead == true) && (x.GetMethod.IsVirtual == true)) || ((x.CanWrite == true) && (x.SetMethod.IsVirtual == true))).Concat(additionalInterfaceTypes.SelectMany(x => x.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(y => ((y.CanRead == true) && (y.GetMethod.IsVirtual == true)) || ((y.CanWrite == true) && (y.SetMethod.IsVirtual == true))))).Distinct().ToList());
}
else
{
return (additionalInterfaceTypes.SelectMany(x => x.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(y => ((y.CanRead == true) && (y.GetMethod.IsVirtual == true)) || ((y.CanWrite == true) && (y.SetMethod.IsVirtual == true)))).Distinct().ToList());
}
}

protected virtual IEnumerable<MethodInfo> GetMethods(Type baseType, Type [] additionalInterfaceTypes)
{
if (baseType != interfaceProxyType)
{
return (baseType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => x.IsFinal == false && (x.IsVirtual == true || x.IsAbstract == true)).Concat(additionalInterfaceTypes.SelectMany(x => x.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(y => y.IsVirtual == true))).Distinct().ToList());
}
else
{
return (additionalInterfaceTypes.SelectMany(x => x.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(y => y.IsFinal == false && (y.IsVirtual == true || y.IsAbstract == true))).Distinct().ToList());
}
}

protected virtual IEnumerable<ConstructorInfo> GetConstructors(Type baseType)
{
return (baseType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));
}

public override Type Generate(IInterceptor interceptor, Type baseType, Type handlerType, params Type[] additionalInterfaceTypes)
{
var properties = this.GetProperties(baseType, additionalInterfaceTypes);
var methods = this.GetMethods(baseType, additionalInterfaceTypes);
var constructors = this.GetConstructors(baseType);

var targetClass = new CodeTypeDeclaration(string.Concat(baseType.Name, "_Dynamic"));
targetClass.IsClass = baseType.IsClass;
targetClass.TypeAttributes = TypeAttributes.Sealed | TypeAttributes.Serializable;
targetClass.BaseTypes.Add((baseType.IsInterface == false) ? baseType : typeof(object));
targetClass.BaseTypes.Add(proxyTypeReference.BaseType);

foreach (var additionalInterfaceType in additionalInterfaceTypes)
{
targetClass.BaseTypes.Add(additionalInterfaceType);
}

var samples = new CodeNamespace(baseType.Namespace);
samples.Imports.Add(new CodeNamespaceImport(typeof(string).Namespace));
samples.Types.Add(targetClass);

var targetUnit = new CodeCompileUnit();
targetUnit.Namespaces.Add(samples);

this.GenerateFields(targetClass, baseType, handlerType, (interceptor != null) ? interceptor.GetType() : null);

this.GenerateConstructors(targetClass, baseType, constructors);

this.GenerateMethods(targetClass, baseType, methods);

this.GenerateProperties(targetClass, baseType, properties);

var builder = new StringBuilder();

using (var sourceWriter = new StringWriter(builder))
{
provider.GenerateCodeFromCompileUnit(targetUnit, sourceWriter, options);
}

var parameters = new CompilerParameters() { GenerateInMemory = true };

this.AddReferences(parameters, baseType, handlerType, (interceptor != null) ? interceptor.GetType() : null, additionalInterfaceTypes);

var results = provider.CompileAssemblyFromDom(parameters, targetUnit);

if (results.Errors.HasErrors == true)
{
throw new InvalidOperationException(string.Join(Environment.NewLine, results.Errors.OfType<object>()));
}

return (results.CompiledAssembly.GetTypes().First());
}
}

Granted, this one is a bit complex, as it needs to generate a whole type dynamically, including constructors to match the base type, overrides for virtual method and properties, and it needs to have them call the interception handler instance. The generated type will also implement the IInterceptionProxy interface, which exposes a Interceptor property, from which you can at runtime access the interceptor. If you are not used to working with the code DOM, have a look at it, you might find it instructive! Winking smile

ClassDiagram1

In the end, here is the code to use it:

public class MyInterceptedType
{
public virtual int Property { get; set; }
public virtual void Method() { /* ... */ }
}

var interceptor = new VirtualMethodInterceptor();
var myProxyType = interceptor.Intercept(type, typeof(MyInterceptionHandler));
var myProxy = Activator.CreateInstance(myProxyType) as MyInterceptedType;
myProxy.Method(); //will get intercepted

The only care that you need to take is to only intercept non-virtual types, with public constructors. Also, the interception handler type must have a public parameterless constructor.

Interface Interception

This one is an instance interceptor. The instance to intercept must implement a given interface. Here is the code of InterfaceInterceptor:

public sealed class InterfaceInterceptor : IInstanceInterceptor
{
private readonly InterceptedTypeGenerator generator;

public InterfaceInterceptor(InterceptedTypeGenerator generator)
{
this.generator = generator;
}

public InterfaceInterceptor() : this(CodeDOMInterceptedTypeGenerator.Instance)
{
}

public object Intercept(object instance, Type typeToIntercept, IInterceptionHandler handler)
{
if (instance == null)
{
throw (new ArgumentNullException("instance"));
}

if (typeToIntercept == null)
{
throw (new ArgumentNullException("typeToIntercept"));
}

if (handler == null)
{
throw (new ArgumentNullException("handler"));
}

if (typeToIntercept.IsInstanceOfType(instance) == false)
{
throw (new ArgumentNullException("instance"));
}

if (typeToIntercept.IsInterface == false)
{
throw (new ArgumentNullException("typeToIntercept"));
}

if (this.CanIntercept(instance) == false)
{
throw (new ArgumentNullException("instance"));
}

var interfaceProxy = this.generator.Generate(this, typeof(InterfaceProxy), null, typeToIntercept);

var newInstance = Activator.CreateInstance(interfaceProxy, this, handler, instance);

return (newInstance);
}

public bool CanIntercept(object instance)
{
return (instance.GetType().GetInterfaces().Length != 0);
}
}

You can see that it also needs to generate code at runtime, therefore it uses the same types InterceptedTypeGenerator and CodeDOMInterceptedTypeGenerator shown earlier. This time, the generated proxy implements the interface to intercept and is a subclass of InterfaceProxy:

public abstract class InterfaceProxy
{
protected readonly object instance;
protected readonly IInterceptor interceptor;
protected readonly IInterceptionHandler handler;

protected InterfaceProxy(IInterceptor interceptor, IInterceptionHandler handler, object instance)
{
this.instance = instance;
this.interceptor = interceptor;
this.handler = handler;
}
}

We use it like this:

public interface IMyInterceptedType
{
int Property { get; set; }
void Method();
}

public class MyInterceptedType : IMyInterceptedType
{
public int Property { get; set; }
public void Method() { /* ... */ }
}

var instance = new MyInterceptedType();
var interceptor = new InterfaceInterceptor();
var myProxy = interceptor.Intercept(instance, typeof(IMyInterceptedType), new MyInterceptionHandler()) as IMyInterceptedType;
myProxy.Method(); //will get intercepted

As you can see, all the intercepted type needs is to implement one interface, it’s this interface that can actually be intercepted, and this is the second parameter to the https://msdn.microsoft.com/en-us/library/ms731082(v=vs.110).aspxmethod.

ClassDiagram2

Transparent Proxy Interceptor

This one is the technique that WCF and .NET Remoting use to communicate to a remote system.

The TransparentProxyInterceptor is an instance interceptor:

public sealed class TransparentProxyInterceptor : IInstanceInterceptor
{
public static readonly IInstanceInterceptor Instance = new TransparentProxyInterceptor();

public object Intercept(object instance, Type typeToIntercept, IInterceptionHandler handler)
{
if (instance == null)
{
throw (new ArgumentNullException("instance"));
}

if (typeToIntercept == null)
{
throw (new ArgumentNullException("typeToIntercept"));
}

if (handler == null)
{
throw (new ArgumentNullException("handler"));
}

if (this.CanIntercept(instance) == false)
{
throw (new ArgumentException("instance"));
}

if (typeToIntercept.IsInstanceOfType(instance) == false)
{
throw (new ArgumentException("typeToIntercept"));
}

var proxy = new TransparentProxy(this, instance, typeToIntercept, handler);

return (proxy.GetTransparentProxy());
}

public bool CanIntercept(object instance)
{
return ((instance is MarshalByRefObject) || (instance.GetType().GetInterfaces().Length != 0));
}
}

Here, we don’t generate any code, we just leverage MarshalByRefObject and RealProxy, which do all the magic. We need a type that extends RealProxy and adds our own IInterceptionProxy:

internal sealed class TransparentProxy : RealProxy, IRemotingTypeInfo, IInterceptionProxy
{
private readonly object instance;
private readonly IInterceptionHandler handler;
private readonly TransparentProxyInterceptor interceptor;

public TransparentProxy(TransparentProxyInterceptor interceptor, object instance, Type typeToIntercept, IInterceptionHandler handler) : base(typeToIntercept)
{
this.instance = instance;
this.handler = handler;
this.interceptor = interceptor;
}

public override IMessage Invoke(IMessage msg)
{
ReturnMessage responseMessage;
object response = null;
Exception caughtException = null;

try
{
var methodName = msg.Properties["__MethodName"] as string;
var parameterTypes = msg.Properties["__MethodSignature"] as Type[];
var parameters = msg.Properties["__Args"] as object[];
var typeName = msg.Properties["__TypeName"] as string;
var method = this.instance.GetType().GetMethod(methodName, parameterTypes);

if (method == null)
{
if (methodName.StartsWith("get_") == true)
{
var property = this.instance.GetType().GetProperty(methodName.Substring(4), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

if (property != null)
{
method = property.GetGetMethod();
}
else
{
if ((methodName == "get_Interceptor") && (typeName == typeof(IInterceptionProxy).AssemblyQualifiedName))
{
return (new ReturnMessage(this.interceptor, null, 0, null, msg as IMethodCallMessage));
}
}
}
}

var args = new InterceptionArgs(this.instance, method, parameters);

this.handler.Invoke(args);

if (args.Handled == false)
{
args.Proceed();
}

response = args.Result;
}
catch (Exception ex)
{
caughtException = ex;
}

var message = msg as IMethodCallMessage;

if (caughtException == null)
{
responseMessage = new ReturnMessage(response, null, 0, null, message);
}
else
{
responseMessage = new ReturnMessage(caughtException, message);
}

return (responseMessage);
}

bool IRemotingTypeInfo.CanCastTo(Type fromType, object o)
{
return (fromType == typeof(IInterceptionProxy));
}

string IRemotingTypeInfo.TypeName
{
get;
set;
}

public IInterceptor Interceptor
{
get { return this.interceptor; }
}
}

Using TransparentProxyInterceptor we can only intercept types that either inherit from MarshalByRefObject or implement some interface, in which case, we can only intercept methods and properties coming from that interface.

Here’s how we would use it with any type (not inheriting from MarshalByRefObject) that exposes an interface:

public interface IMyInterceptedType
{
int Property { get; set; }
void Method();
}

public class MyInterceptedType : IMyInterceptedType
{
public int Property { get; set; }
public void Method() { /* ... */ }
}

var instance = new MyInterceptedType();
var interceptor = new TransparentProxyInterceptor();
var canIntercept = interceptor.CanIntercept(instance);
var myProxy = interceptor.Intercept(instance, typeof(IMyInterceptedType), new MyInterceptionHandler()) as IMyInterceptedType;
var proxy = myProxy as IInterceptionProxy;
var otherInterceptor = proxy.Interceptor;
myProxy.Method(); //will get intercepted

Again, all it takes is an interface to intercept.

Context Bound Object Interceptor

Since its early days, .NET introduced an interception mechanism that doesn’t need any coding. The problem is that it requires inheriting from a particular base class (ContextBoundObject) plus adding an attribute to it (ContextAttribute). I include this technique here for completeness, since it is usually not that useful.

The ContextBoundInterceptor looks like this:

public sealed class ContextBoundObjectInterceptor : IInstanceInterceptor
{
public static readonly IInstanceInterceptor Instance = new ContextBoundObjectInterceptor();

internal static readonly ConcurrentDictionary<ContextBoundObject, IInterceptionHandler> interceptors = new ConcurrentDictionary<ContextBoundObject, IInterceptionHandler>();

public object Intercept(object instance, Type typeToIntercept, IInterceptionHandler handler)
{
if (!(instance is ContextBoundObject))
{
throw new ArgumentException("Instance is not a ContextBoundObject.", "instance");
}

interceptors[instance as ContextBoundObject] = handler;

return (instance);
}

public bool CanIntercept(object instance)
{
return ((instance is ContextBoundObject) && (Attribute.IsDefined(instance.GetType(), typeof(InterceptionContextAttribute))));
}

public static IInterceptionHandler GetInterceptor(ContextBoundObject instance)
{
foreach (var @int in interceptors)
{
if (@int.Key == instance)
{
return @int.Value;
}
}

return null;
}
}

We need to use a trick here to associate an interceptor with an intercepted instance, in the form of a static dictionary. In order to make this work, we also need the InterceptionContextAttribute:

[Serializable]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class InterceptionContextAttribute : ContextAttribute
{
public InterceptionContextAttribute() : base("InterceptionContext")
{
}

public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
{
ctorMsg.ContextProperties.Add(new InterceptionProperty());
base.GetPropertiesForNewContext(ctorMsg);
}

public override bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg)
{
var p = ctx.GetProperty(InterceptionProperty.InterceptionPropertyName) as InterceptionProperty;
return (p != null);
}

public override bool IsNewContextOK(Context newCtx)
{
var p = newCtx.GetProperty(InterceptionProperty.InterceptionPropertyName) as InterceptionProperty;
return (p != null);
}
}

And the InterceptionProperty:

internal sealed class InterceptionProperty : IContextProperty, IContributeObjectSink
{
internal static readonly string InterceptionPropertyName = "Interception";

public string Name
{
get
{
return InterceptionPropertyName;
}
}

public bool IsNewContextOK(Context newCtx)
{
var p = newCtx.GetProperty(this.Name) as InterceptionProperty;
return p != null;
}

public void Freeze(Context newContext)
{
}

public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)
{
return new InterceptionMessageSink(obj as ContextBoundObject, nextSink);
}
}

Much more work that it deserves, I’d say. Anyway, here’s a sample usage:

public interface IMyInterceptedType
{
int Property { get; set; }
void Method();
}

[InterceptionContext]
public class MyInterceptedType : ContextBoundObject, IMyInterceptedType
{
public int Property { get; set; }
public void Method() { /* ... */ }
}

var instance = new MyInterceptedType();
var interceptor = new ContextBoundObjectInterceptor();
var myProxy = interceptor.Intercept(instance, null, new MyInterceptionHandler()) as IMyInterceptedType;
myProxy.Method(); //will get intercepted

As I said, this technique is only listed here for completeness’ sake, I don’t expect you to be using it much.

Dynamic Interceptor

Final technique, this time, we will be making use of the dynamic functionality introduced in .NET 4.

First, the DynamicInterceptor class, another instance interceptor:

public sealed class DynamicInterceptor : IInstanceInterceptor
{
public static readonly IInstanceInterceptor Instance = new DynamicInterceptor();

public object Intercept(object instance, Type typeToIntercept, IInterceptionHandler handler)
{
return new DynamicProxy(this, instance, handler);
}

public bool CanIntercept(object instance)
{
return true;
}
}

Simple, right? And here is the DynamicProxy:

internal sealed class DynamicProxy : DynamicObject, IInterceptionProxy
{
private readonly IInterceptionHandler handler;
private readonly object target;

public DynamicProxy(IInterceptor interceptor, object target, IInterceptionHandler handler)
{
if (interceptor == null)
{
throw new ArgumentNullException("target");
}

if (target == null)
{
throw new ArgumentNullException("target");
}

if (handler == null)
{
throw new ArgumentNullException("handler");
}

this.Interceptor = interceptor;
this.target = target;
this.handler = handler;
}

public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
var method = this.target.GetType().GetMethod(binder.Name);
var arg = new InterceptionArgs(this.target, method, args);

this.handler.Invoke(arg);

if (arg.Handled == true)
{
result = arg.Result;
}
else
{
result = method.Invoke(this.target, args);
}

return true;
}

public IInterceptor Interceptor { get; private set; }
}

This one inherits from DynamicObject, which allows us to find properties and methods at runtime.

Finally, its usage:

public class MyInterceptedType
{
public int Property { get; set; }
public void Method() { /* ... */ }
}

var instance = new MyInterceptedType();
var interceptor = new DynamicInterceptor();
//here: using dynamic instead of MyInterceptedType!
dynamic myProxy = interceptor.Intercept(instance, null, new MyInterceptionHandler());
myProxy.Method(); //will get intercepted

No constraints whatsoever in what you can intercept, you need only to remember to declare your proxy as dynamic instead of an actual type.

Interception Handlers

Looks cool, right? What about interception handlers – the parameter that you pass to one of the Intercept methods? Well, it is agnostic as to what interception technique you use and just needs to implement IInterceptionHandler. Its Invoke method is called with a parameter that contains all the information that you need. From there you can do some stuff before calling letting the normal flow proceed, change the arguments to the intercepted method, change the return value (in the case of non-void intercepted methods), do stuff after calling the intercepted method or not call it at all:

public void Invoke(InterceptionArgs arg)
{
arg.Parameters[1] = 120; //changed the original parameters

Console.WriteLine("Before execution");

arg.Proceed(); //call the intercepted method

Console.WriteLine("After execution");

arg.Result = "some result"; //changed the return value

arg.Handled = true; //signal to other interceptors that it has been handled
}

Here is a simple implementation that retries a method call a couple of times in case it throws an exception:

public sealed class RetriesInterceptionHandler : IInterceptionHandler
{
public RetriesInterceptionHandler()
{
this.Retries = 3;
this.Delay = TimeSpan.FromSeconds(5);
}

public int Retries { get; set; }
public TimeSpan Delay { get; set; }

public void Invoke(InterceptionArgs arg)
{
for (var i = 0; i < this.Retries; i++)
{
try
{
arg.Proceed();
break;
}
catch
{
if (i != this.Retries - 1)
{
Thread.Sleep(this.Delay);
}
else
{
throw;
}
}
}
}
}

When it is asked to intercept a method call it loops a number of times, tries to call the default implementation, and, in the event of an exception, sleeps for a while and tries again. If it reached the maximum number of retries, it just throws the caught exception.

Another common scenario is to use attributes as Aspects. We just need to implement an interception handler that looks at interception attributes:

public sealed class AttributesInterceptionHandler : IInterceptionHandler
{
public static readonly IInterceptionHandler Instance = new AttributesInterceptionHandler();

public void Invoke(InterceptionArgs arg)
{
var attrs = arg.Method.GetCustomAttributes(true).OfType<InterceptionAttribute>().OrderBy(x => x.Order).Cast<IInterceptionHandler>();

foreach (var attr in attrs)
{
attr.Invoke(arg);
}
}
}

Where an InterceptionAttribute base class could be implemented as this:

[Serializable]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public abstract class InterceptionAttribute : Attribute, IInterceptionHandler
{
public int Order { get; set; }

public abstract void Invoke(InterceptionArgs arg);
}

You can specify an order by which you want interception attributes to execute.

Yet another technique, using a registry to associate the methods to intercept with their desired handler:

public sealed class RegistryInterceptionHandler : IInterceptionHandler
{
private readonly Dictionary<MethodInfo, IInterceptionHandler> handlers = new Dictionary<MethodInfo, IInterceptionHandler>();

public RegistryInterceptionHandler Register(Type type, string methodName, IInterceptionHandler handler)
{
foreach (var method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
{
this.Register(method, handler);
}

return this;
}

public RegistryInterceptionHandler Register(MethodInfo method, IInterceptionHandler handler)
{
if (method == null)
{
throw new ArgumentNullException("method");
}

if (handler == null)
{
throw new ArgumentNullException("handler");
}

this.handlers[method] = handler;

return this;
}

private RegistryInterceptionHandler RegisterExpressionMethod(Expression expression, IInterceptionHandler handler)
{
if (expression is MethodCallExpression)
{
return this.Register((expression as MethodCallExpression).Method, handler);
}
else if (expression is UnaryExpression)
{
if ((expression as UnaryExpression).Operand is MethodCallExpression)
{
return this.Register(((expression as UnaryExpression).Operand as MethodCallExpression).Method, handler);
}
}
else if (expression is MemberExpression)
{
if ((expression as MemberExpression).Member is PropertyInfo)
{
this.Register(((expression as MemberExpression).Member as PropertyInfo).GetMethod, handler);
return this.Register(((expression as MemberExpression).Member as PropertyInfo).SetMethod, handler);
}
}

throw new ArgumentException("Expression is not a method call", "method");
}

public RegistryInterceptionHandler Register<T>(Expression<Func<T, object>> method, IInterceptionHandler handler)
{
return this.RegisterExpressionMethod(method.Body, handler);
}

public RegistryInterceptionHandler Register<T>(Expression<Action<T>> method, IInterceptionHandler handler)
{
return this.RegisterExpressionMethod(method.Body, handler);
}

public void Invoke(InterceptionArgs arg)
{
IInterceptionHandler handler;

if (this.handlers.TryGetValue(arg.Method, out handler) == true)
{
handler.Invoke(arg);
}
}
}

Here you would have to explicitly register the methods you want to intercept (also works for properties), and, for each of these methods, its interception handler:

var registry = new RegistryInterceptionHandler();
registry.Register<IMyInterceptedType>(x => x.Method(), new MyInterceptionHandler());

All you need to do is implement Invoke, and off you go! To make it clear, any of these three implementations – RetriesInterceptionHandlerAttributesInterceptionHandler and RegistryInterceptionHandler – should be passed as the third argument to IInstanceInterceptor.Intercept:

var instance = new MyInterceptedType();
var interceptor = new InterfaceInterceptor();
var proxy = interceptor.Intercept(instance, typeof(IMyInterceptedType), new RetriesInterceptionHandler { Retries = 3, Delay = TimeSpan.FromSeconds(5) }) as IMyInterceptedType;

Conclusion

DevelopmentWithADot.Interception

I’ve shown some techniques that can be used to intercept .NET code in a number of scenarios. You basically need to pick your interceptor based on what you’ve got:

  • If you want to intercept a type (not an instance) that is not sealed or is abstract, you need to use VirtualMethodInterceptor, as it is the only type interceptor
  • If you want to intercept a particular interface, you can use one of InterfaceInterceptor, TransparentProxyInterceptor or DynamicInterceptor
  • If you want to intercept an instance of a class inheriting directly or indirectly from MarshalByRefObject, regardless of whether or not it implements some interface, you can use TransparentProxyInterceptor
  • If you want to intercept some type, whatever its base class or implemented interfaces are, and you are cool with using the dynamic keyword around it, use DynamicInterceptor
  • If you are OK with having a base class of ContextBoundObject, and you also don’t mind adding an obscure attribute to your class, you have ContextBoundObjectInterceptor 

Other scenarios, like intercepting static methods, can only be solved through IL weaving. Have a look at my previous post about it and see if it helps you.

Keep in mind that this code works as it should but it is not bulletproof. Let me know if you face any problems using it, or if you have any questions or remarks!