Skip to content

Instantly share code, notes, and snippets.

@VladimirMakaev
Created May 21, 2014 12:44
Show Gist options
  • Select an option

  • Save VladimirMakaev/0872cdce47b9ffb6e825 to your computer and use it in GitHub Desktop.

Select an option

Save VladimirMakaev/0872cdce47b9ffb6e825 to your computer and use it in GitHub Desktop.
Autofac RegistrationSource to automatically inject IDbSet<T>
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Reflection;
using Anybill.Renaissance.Common;
using Autofac;
using Autofac.Builder;
using Autofac.Core;
namespace Anybill.Renaissance.Hosts.Composition.Modules.Sources
{
/// <summary>
/// Makes sure the <see cref="IDbSet{TEntity}" /> is properly registered.
/// It's actually resolved using <code>context.Resolve&lt;DbContext&gt;().Set&lt;TEntity&gt;()</code>
/// </summary>
public class DbSetRegistrationSource : IRegistrationSource
{
private static readonly MethodInfo createDbSetRegistrationMethod =
typeof(DbSetRegistrationSource).GetMethod("CreateDbSetRegistration",
BindingFlags.Static | BindingFlags.NonPublic);
private readonly Func<IComponentContext, DbContext> contextResolver;
/// <summary>
/// Initializes a new instance of the <see cref="DbSetRegistrationSource"/> class.
/// </summary>
/// <param name="contextResolver">Specifies who to get the underlying DbContext to get the IDbSet from</param>
public DbSetRegistrationSource(Func<IComponentContext, DbContext> contextResolver)
{
this.contextResolver = contextResolver;
}
/// <summary>
/// Gets whether the registrations provided by this source are 1:1 adapters on top
/// of other components (I.e. like Meta, Func or Owned.)
/// </summary>
public bool IsAdapterForIndividualComponents
{
get
{
return false;
}
}
/// <summary>
/// Retrieve registrations for an unregistered service, to be used
/// by the container.
/// </summary>
/// <param name="service">The service that was requested.</param>
/// <param name="registrationAccessor">A function that will return existing registrations for a service.</param>
/// <returns>
/// Registrations providing the service.
/// </returns>
/// <remarks>
/// If the source is queried for service s, and it returns a component that implements both s and s', then it
/// will not be queried again for either s or s'. This means that if the source can return other implementations
/// of s', it should return these, plus the transitive closure of other components implementing their
/// additional services, along with the implementation of s. It is not an error to return components
/// that do not implement
/// <paramref name="service" />.
/// </remarks>
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var swt = service as IServiceWithType;
if (swt == null || !swt.ServiceType.IsGenericType
|| swt.ServiceType.GetGenericTypeDefinition() != typeof(IDbSet<>))
{
yield break;
}
Type valueType = swt.ServiceType.GetGenericArguments()[0];
MethodInfo registrationCreator = createDbSetRegistrationMethod.MakeGenericMethod(valueType);
yield return (IComponentRegistration)registrationCreator.Invoke(null,
new object[]
{
service,
contextResolver
});
}
[UsedImplicitly]
private static IComponentRegistration CreateDbSetRegistration<T>(
Service providedService,
Func<IComponentContext, DbContext> contextResolver) where T : class
{
IRegistrationBuilder<DbSet<T>, SimpleActivatorData, SingleRegistrationStyle> rb = RegistrationBuilder.
ForDelegate((c, p) =>
{
DbContext dbContext = contextResolver(c);
return dbContext.Set<T>();
}).As(providedService);
return rb.CreateRegistration();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment