Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unity Container RegisterType<TFrom, TTo> Issues

I'm trying to do auto registration of repositories:

This works but I don't like it because, in the Service layer classes I will have to supply the concrete EntityRepository<T> to the constructors instead of supply the interface IRepository<T>

public static IContainer RegisterRepositories(
    this IContainer container, params Assembly[] assemblies)
{
    var repositories = (
        from assembly in assemblies
        from type in assembly.GetTypes()
        where typeof(ObjectContext).IsAssignableFrom(type)
        from property in type.GetProperties()
        let propertyType = property.PropertyType
        where propertyType.IsGenericType 
        where propertyType
            .GetGenericTypeDefinition() == typeof(ObjectSet<>)
        select new 
        {
            ObjectContextType = type, 
            DomainType = propertyType.GetGenericArguments()[0] 
        })
        .ToList();

    foreach (var repository in repositories)    
    {
        container.RegisterType(typeof(BaseObjectContext), 
            new PerExecutionContextLifetimeManager());

        var serviceType = typeof(IRepository<>)
            .MakeGenericType(repository.DomainType);

        var implementationType = typeof(EntityRepository<>)
            .MakeGenericType(repository.ObjectContextType);

        container.RegisterType(serviceType, implementationType, 
            new TransientLifetimeManager());
    };

    return container;
}

I want to automatically register my repositories using container.RegisterType<TFrom, TTo>(new TransientLifetimeManager()); I know where the problem is, because this way of registering requires you to supply the real name of the interface and class such as IRepository<Customer> and EntityRepository<Customer>. I can do that but I would have to list the repositories one by one, and I don't think it's feasible if you are dealing with a project of 100 Domain objects. I want to do them automatically like this so I can inject the IRepository<T> in the Service layer classes.

// Configure root container.Register types and life time 
// managers for unity builder process
public static IContainer RegisterRepositories(
    this IContainer container, params Assembly[] assemblies)
{
    var repositories = (
        from assembly in assemblies
        from type in assembly.GetTypes() 
        where typeof(ObjectContext).IsAssignableFrom(type)
        from property in type.GetProperties()
        let propertyType = property.PropertyType
        where propertyType.IsGenericType 
        where propertyType
            .GetGenericTypeDefinition() == typeof(ObjectSet<>)
        select new 
        {
            ObjectContextType = type, 
            DomainType = propertyType.GetGenericArguments()[0] 
        })
        .ToList()

    foreach (var repository in repositories)
    {
        container.RegisterType<BaseObjectContext>(
            new PerExecutionContextLifetimeManager(), 
            new InjectionConstructor());

        container.RegisterType<IRepository<repository.DomainType>,
             EntityRepository<repository.ObjectContextType>>(
            new TransientLifetimeManager());
    };

    return container;
}
like image 330
Mohamed Avatar asked Sep 12 '26 13:09

Mohamed


1 Answers

I'm not really sure what the problem is that you are trying to solve, but I don't think that your design will work in the long run. What is clear to me however, is that you have a big domain, and want to be able to inject IRepository<T> instances in service classes, but want to take advantage of batch registration abd the Entity Framework code generator, to prevent you from having to having to maintain a lot of code, which is an admirable goal.

You however try to inject IRepository<T> instances into services. Although repository is a common design pattern, injecting repositories directly into services never worked for me and seems odd in the context of Entity Framework, that uses the unit of work pattern (the ObjectContext) to centralize operations between the application and the database. Injecting repositories, means that under each repository must lay an ObjectContext and you probably want to have the same ObjectContext in all repository instances that you inject in a single service, because your operations should probably be atomic.

IMO it would be better not to inject IRepository<T> instances into your services, but to inject a UnitOfWork or even better: an IUnitOfWorkFactory. The IUnitOfWorkFactory can than create a new UnitOfWork instance (that the service must dispose after its done) and the UnitOfWork would hold a list of IRepository<T> instances, and contain a SubmitChanges method.

By implementing a IRepository<T> GetRepository<T>() method on the UnitOfWork, you can effectively get a specific repository without having to do any batch registration what so ever to wire up your 100 repositories. This can be done inside the IUnitOfWork implementation for you.

From an application perspective it would be even nicer if that UnitOfWork contains properties for specific IRepository<T> implementations, such as public IRepository<Customer> Customers { get; set; }, but that does mean that you would need to add new properties when new entities are added to the system, which is probably not something you like. However, in my experience this doesn’t hurt the maintainability much, while it improves the readability of your code greatly.

This design might look a bit far fetched to you, but I actually have a similar design running in production for over half a year now and it works perfectly. I have blogged about this (see Faking your LINQ provider) and others have adopted this approach successfully in their applications. It seems to work for others as well. Perhaps it is interesting for you, or it could at least give you some ideas how to effectively do this. I use dependency injection and with the given design I just have to configure a few types to get this running.

I hope this helps.

like image 126
Steven Avatar answered Sep 15 '26 04:09

Steven