Reputation: 21304
I am attempting to use the following Generic Repository Interface for DI and constructor injection:
public interface IRepository<TEntity> : IDisposable where TEntity : class
The problem is in order to define an instance of the Interface, I must provide the class type like this:
private IRepository<Person> _personRepository;
The issue with this is if I'm using DI (and I'm using Unity for IoC framework), then I have to define multiple instances in my constructor to get all repository interfaces I need to work with like this:
public MyClass(IRepository<Person> personRepository,
IRepository<Orders> ordersRepository,
IRepository<Items> itemsRepository,
IRepository<Locations> locationsRepository)
{
_personRepository = personRepository;
_OrdersRepository = ordersRepository;
_itemsRepository = itemsRepository;
_locationsRepository = locationsRepository;
}
Questions:
Please help clear this up for me, and I appreciate all your help!
Upvotes: 16
Views: 14859
Reputation: 27009
I needed to do this using configuration file. It's actually pretty easy but took me a while to figure it out.
In this example we have an interface IRepository<T>
and it has 2 implementations:
OneRepository
TwoRepository
We then have a class named Worker
which depends on IRepository<One>
and IRepository<Two>
. We ask unity to create an instance of Worker
for us and figure out the dependencies from the config file.
Interface and Implementation
All of these are in the namespace ConsoleApplication1
in this example.
public class Worker
{
private readonly IRepository<One> one;
private readonly IRepository<Two> two;
public Worker(IRepository<One> one, IRepository<Two> two)
{
this.one = one;
this.two = two;
}
public string DoOne()
{
return this.one.Add(new One());
}
public string DoTwo()
{
return this.two.Add(new Two());
}
}
public interface IRepository<T>
{
string Add(T t);
}
public class OneRepository : IRepository<One>
{
public string Add(One t)
{
return "One";
}
}
public class TwoRepository : IRepository<Two>
{
public string Add(Two t)
{
return "Two";
}
}
public class One { }
public class Two { }
Unity Configuration
Please note we instruct unity and tell it the name of the assembly. Then we register the 2 implementations.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<assembly name="ConsoleApplication1" />
<container>
<register type="ConsoleApplication1.IRepository[[ConsoleApplication1.One]]" mapTo="ConsoleApplication1.OneRepository" />
<register type="ConsoleApplication1.IRepository[[ConsoleApplication1.Two]]" mapTo="ConsoleApplication1.TwoRepository" />
</container>
</unity>
</configuration>
Application
This is the Composition Root.
public class Program
{
static void Main()
{
UnityContainer container = new UnityContainer();
var res = container.LoadConfiguration();
Worker worker = res.Resolve<Worker>();
Console.WriteLine(worker.DoOne());
Console.WriteLine(worker.DoTwo());
Console.Read();
}
}
The output as expected is:
One
Two
Upvotes: 0
Reputation: 513
As noted by D Stanley, a dependency must be a concrete interface. Otherwise, where are you going to declare T? Your dependent class could be generic, but you still have to say "T is a Person" at some point.
That said, Unity handles registering generic types pretty nicely.
Let's say you implement IRepository<T>
with a generic class Repository<T>
that wraps a DbSet<T>
(or whatever).
The following registration and resolves will then work (which includes injecting into any constructors):
container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
// no specific registration needed for the specific type resolves
container.Resolve(<IRepository<Person>);
container.Resolve(<IRepository<Order>);
If you needed a specific override of a type (says the Items repository is special for whatever reason so it has a fully implemented ItemRepository
class), just register that specific implementation after the generic one:
container.RegisterType<IRepository<Item>, ItemRepository>();
Resolving IRespository<Item>
will now get your specific implementation.
For the record, I think this can only be done in code, and not configuration files. Someone feel free to correct that assumption.
Upvotes: 11
Reputation: 14919
This seems ok except one missing point; you need UnitOfWork pattern to enable transactions. Without UnitOfWork pattern applied, all repositories try to commit db operations on different contexts.
Upvotes: 2
Reputation: 39277
You could consider using an Aggregate Service that bundles several other services together, but you should also look carefully to see if your MyClass
is trying to do too much; having a very large number of dependencies can be a indication of that.
Upvotes: 2
Reputation: 152521
Is this OK?
Sure. There's personal preference on whether to use constructor injection like you have or property injection. Constructor injection is cleaner since you don't have to have lot of parameters to your constructor, but it's safer as well.
what's the point of Unity to register Interface to concrete type
One reason is so that you can unit test MyClass
without having to use your actual repository that hits a database. You can "fake" out the repository to return hard-coded values to test against.
Upvotes: 2