Using generic interface in the repository pattern - c#

I am currently developing an MVC 3 application with EF and the Repository pattern. I created a generic interface as follows:
interface IGenericRepository<T>
{
}
And an abstract class as follows:
abstract class GenericRepository<TContext ,T> : IGenericRepository<T>
where TContext : DbContext, new()
{
}
After that my repo inherits both of them like this:
interface ICardRepository : IGenericRepository<Card>
{
}
and
class CardRepository : GenericRepository<EfContext, Card>,
ICardRepository
{
}
With Unity I register the interface and the class like this:
container.RegisterType<ICardRepository, CardRepository>();
Now the question is: can I use IGenericRepository instead of ICardRepository?
Like this:
container.RegisterType<IGenericRepository<Card>, CardRepository>();
In theory I can, but as I still do not get how this pattern works I am not quite sure if I am not breaking or missing something.

One possible issue is now you will have to resolve IGenericRepository<Card>, instead of ICardRepository.
This will not work:
var cardRepository = container.Resolve<ICardRepository>();
Instead you will have to do this:
var cardRepository = container.Resolve<IGenericRepository<Card>>();
This also means that if you are doing something like constructor injection, you can't use:
public class SomethingDependentOnCardRepository
{
// The parameter type should be IGenericRepository<Card> instead,
// if you are using Unity to resolve this dependency.
public SomethingDependentOnCardRepository(ICardRepository cardRepository)
{
// code
}
}

Related

Making covariant interface backward compatible

We have an interface to deal with DAL with pretty simple definition:
interface IRepository<T> : IQueriable<T> // so we can read data from database
{
Save(T document); // dozen of methods here
}
Mostly we use two implementations: real version and in memory version for unit testing. Here is declarations of one of class:
public RealRepository : IRepository<AccountEntity> { ... }
// typical IOC usage
services.AddSingleton<IRepository<AccountEntity>, RealRepository<AccountEntity>>();
Now we are working to spin off for main codebase to custom version of project and we need custom fields in data and occassional custom behavior in repository. Most of classes are fine with base implementation but others would require specific implementation. So my goal is to get to following services in:
var repository = new RealRepository<CustomAccountEntity>();
services.AddSingleton(IRepository<AccountEntity>, repository);
// for new classes
services.AddSingleton(IRepository<CustomAccountEntity>, repository);
I tried to add out T to IRepository but I am using T in input parameters and this gave compile time "Invalid variance" error.
I can see a solution by adding second type parameter to interface so it looks like:
IRepository<TBase, out TChild> : IQueriable<TChild> {
Save (T document);
}
Finally, Question: How can make change 100% backward compatible?
What I tried:
Add IRepository<T>: IRepository<T,T> -> complies, but RealRepository is not implementing IRepository anymore.
Add 2 interfaces in implementation: public class RealRepository<TBase, TChild>: IRepository<TBase, TChild>, IRepository<TChild> but this gives compliation error 'cannot implement both ... and ... because they may unify for some type parameter substitutions'
Save(T document) has T in a contravariant position. That means in T, not out T.
Let's recap what contravariance means. Suppose you had this code:
using System;
public class Entity {}
public class AccountEntity : Entity {}
public class CustomAccountEntity : AccountEntity {}
public interface IQueryable<in T>
where T : Entity
{}
public interface IRepository<in T>
where T : Entity
{
void Save(T record);
}
public class EntityRepository<T> : IRepository<T>
where T : Entity
{
public void Save(T record) {}
}
public class Program
{
public static void Main()
{
// This is ***VALID***:
IRepository<CustomAccountEntity> repo = new EntityRepository<AccountEntity>();
Console.WriteLine(repo == null ? "cast is invalid" : "cast is valid");
}
}
https://dotnetfiddle.net/cnEdcm
So whenever you need a IRepository<CustomAccountEntity>, you can use a concrete EntityRepository<AccountEntity> instance. Seems counter-intuitive, but it's actually totally right: If the concrete method is Save(AccountEntity), it can obviously handle CustomAccountEntity instances too; OTOH if the concrete method were Save(CustomAccountEntity), it would NOT be able to handle simple AccountEntity instances.
Having said that, then I think you should
Use contravariance instead;
Declare all dependencies using the most specialised type, e.g. IRepository<CustomWhateverEntity>;
In the IoC registration code, for each particular entity, setup either Repository<CustomeWhateverEntity>, if you need the extra behaviour, or just Repository<WhateverEntity> otherwise.

Autofac register specific config class in generic repository

I have a generic repository that I want to register with a specific config so it works kind of like a strategy pattern. This is the repository:
public class ConfigProvider<T> : IConfigProvider<T> where T : class
public ConfigProvider(IConfigFilePaths filePaths)
{
_filePaths = filePaths;
}
and those are my two config classes that are used to parametrize the ConfigProvider
public interface IConfigFilePaths
{
...
}
public class DefaultContractTypePaths : IConfigFilePaths
{
...
}
public class TranslationPaths : IConfigFilePaths
{
...
}
So what i would like to do on the dependencies config is something like this:
builder.RegisterType<ConfigProvider<TranslationSet>>().As<IConfigProvider<TranslationSet>>();
builder.UseForType<ConfigProvider<TranslationSet>>().The<TranslationPaths>(); // this obviously does not work
Does anybody know how to tell autofac that it should use TranslationPaths for the ConfigProvider<TranslationSet> without introducing a new interface to mark the configs?
You could use a Named parameter as follows:
builder
.RegisterType<ConfigProvider<TranslationSet>>()
.As<IConfigProvider<TranslationSet>>()
.WithParameter("filePaths", new TranslationPaths());

Problems with Dependency Injection over MVC

I'm trying to implement the Dependency Injection from Core on my software in order to replace Ninject and update everything to our new technology.
Btw, I'm facing a problem on some interfaces that are generic. For such cases I'm getting directly an Exception that the injector could not create an instance of my class.
I inserted above a small snippet of a sample case that puts me on fire.
services.AddTransient(typeof(IRepository), typeof(MyRepository<,>))
Is that way correct? How can I do that?
Class implementation:
public class MyRepository<TEntity, TContext> : IRepositoryBase
where TEntity : class
where TContext : IDbContext, new()
{
...
}
Interface:
public interface IRepository : IDisposable
{
...
}
Thanks!
This doesn't really make sense. You will be asking the container for IRepository, so how would it know what the generic type arguments should be such that it can give you a MyRepository<,>?
So when asked to return an object like this:
public class MyService
{
private IRepository<Something, SomethingElse> _repo;
public MyService(IRepository<Something, SomethingElse> repo)
{
// Container will actually give us MyRepository<Something, SomethingElse>
_repo = repo;
}
}
I would expect either:
services.AddTransient(typeof(IRepository<,>), typeof(MyRepository<,>));
or, if your repository doesn't need to be generic (I don't understand why it'd need two generic arguments as it is), then I'd expect this:
services.AddTransient(typeof(IRepository), typeof(MyRepository));
However, since there's no generics involved here, you could use the alternative form to achieve the same thing with less typing:
services.AddTransient<IRepository, MyRepository>();
So really the answer is to solve your interface/class design. Showing more of the implementation of them would help.
UPDATE
Your implementation needs to be:
Class implementation:
public class MyRepository<TEntity, TContext> : IRepository<TEntity, TContext>
where TEntity : class
where TContext : IDbContext, new()
{
...
}
Interface:
public interface IRepository<TEntity, TContext> : IDisposable
where TEntity : class
where TContext : IDbContext, new()
{
...
}
I ended up using Autofac and without any changes on my structure everything started working again.
Will wait a little more for documentation and more people using, so I can change my implementation to use MS DI.
To register all repositories use this:
var allRepositories = GetType().GetTypeInfo()
.Assembly.GetTypes().Where(p =>
p.GetTypeInfo().IsClass &&
!p.GetTypeInfo().IsAbstract &&
typeof(IRepository).IsAssignableFrom(p));
foreach (var repo in allRepositories)
{
var allInterfaces = repo .GetInterfaces();
var mainInterfaces = allInterfaces.Except
(allInterfaces.SelectMany(t => t.GetInterfaces()));
foreach (var itype in mainInterfaces)
{
services.AddScoped(itype, repo);
}
}
Then resolve it:
public YourClass(IRepository<T> repo)
{
//...
}

How to set a constraint for a type so it must be of another generic typed type

This is probably asked before but I can't work it out. Maybe if I could get the title right I could goolge it.
I have got this generic repository interface:
public interface IRepository<TEntity>
{
TEntity Resolve<TEntity>(); // dummy function, just to get the idea
}
I also have a generic unit of work, which is able to resolve a generic repository:
public interface IUnitOfWork
{
IRepository<TEntity> GetGenericRepository<TEntity>() where TEntity : class;
}
So far so good.
But as real life continues, I would like to create a custom repository, with some specific funtions. So I was thinking: inheritance; like this:
public class SpecialRepository : IRepository<SomeEntityType>
{
public void SomeSpecialFunction() { };
}
Obviously, this type cannot be resolved with the GetGenericRepository methode so I thought: lets add a extra method to the IUnitOfWork interface:
public interface IUnitOfWork
{
//same old get generic repository
IRepository<TEntity> GetGenericRepository<TEntity>() where TEntity : class;
//the newly added.
T GetInheretedRepository<T>() where T : class;
}
I want to be able to call the unit of work with the special repository, something like this:
public test()
{
IUnitOfWork uow = new UnitOfWork();
//I want to make this call with a constraint on TemplateRepo
//to enforce it's type: IRepository<T> (which TemplateRepo is)
var y = uow.GetInheretedRepository<TemplateRepo>();
}
The question is: how can I restrict type T in T GetInheretedRepository<T>() where T : class; to be of type: IRepository<TEntity>?
I tried this:
public interface IUnitOfWork
{
//the newly added.
//error: Only class or interface could be specified as constraint
T GetInheretedRepository<T>() where T : class, IRepository; }
and
public interface IUnitOfWork
{
//the newly added.
//error: type argument missing
T GetInheretedRepository<T>() where T : class, IRepository<>;
}
that doesnt work.
I could drop the constrain as a quick-fix or perhaps create an inherited unit of work, but then; the question still remains.
The way to do this is by adding a second generic type argument, as follows:
TRepository GetInheretedRepository<TRepository, TEntity>()
where TRepository : IRepository<TEntity>
where TEntity : class;
Here you supply both the Repository type and the entity type. This way the C# compiler can check whether or not the type matches. Here's how to call it:
var rep = uow.GetInheretedRepository<SpecialRepository, SomeEntityType>();
rep.SomeSpecialFunction();
This obviously sucks, since you will have to specify both types. But more importantly, this sucks because you have to specify the concrete type, making your code take a dependency on a concrete type; a violation of the Dependency Inversion Principle.
I really would like to advice to to step away from a design where you depend on a concrete type, or even better, step away from a design where you have many methods on a specific repository class, because this violates both SRP, OCP and ISP and this will likely cause maintenance problems later on.
So instead, take a look at the application design that is described in this article.
You need to specify second Type like
public interface IUnitOfWork
{
//the newly added.
T GetInheretedRepository<T, TEntity>() where T : class, IRepository<TEntity>;
}
public interface IRepository<TEntity>
{
TEntity Resolve(); // dummy function, just to get the idea
}
example that compiles fine - https://dotnetfiddle.net/MmmPil

How to register many for open generic in Autofac

I'm new to Autofac (not to DI). Here is the situation:
I have these interfaces:
public interface IQuery<out TResult> : IQuery { }
public interface IQueryHandler<in TQuery, out TResult> where TQuery : IQuery<TResult> {
TResult Handle(TQuery query);
}
and there is a lot of implementation of them in my solution:
class GetPersonQuery : IQuery<PersonModel> { }
class GetPersonQueryHandler : IQueryHandler<GetPersonQuery, PersonModel> { }
class GetArticleQuery : IQuery<ArticleModel> { }
class GetArticleQueryHandler : IQueryHandler<GetArticleQuery, ArticleModel> { }
class GetSomethingQuery : IQuery<IEnumerable<SomeModel>> { }
class GetSomethingQueryHandler : IQueryHandler<GetSomethingQuery, IEnumerable<SomeModel>> { }
and so on. I'm currently registering them like this:
builder.RegisterType<GetPersonQueryHandler>()
.As<IQueryHandler<GetPersonQuery, PersonModel>>();
builder.RegisterType<GetArticleQueryHandler>()
.As<IQueryHandler<GetArticleQuery, ArticleModel>>();
builder.RegisterType<GetSomethingQueryHandler>()
.As<IQueryHandler<GetSomethingQuery, SomeModel>>();
// blah blah blah
As you can see, I have a many same registrations. In SimpleInjector (which I was using before), I could register all of them by a single line:
container.RegisterManyForOpenGeneric(
typeof(IQueryHandler<,>),
AppDomain.CurrentDomain.GetAssemblies());
Is it possible to do this stuff in Autofac?
You can do this with Autofac you just need to use the scanning feature and use the AsClosedTypesOf method:
AsClosedTypesOf(open) - register types that are assignable to a closed instance of the open generic type.
So your registration will look like this:
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
.AsClosedTypesOf(typeof (IQueryHandler<,>)).AsImplementedInterfaces();
If you have a single concrete generic type, and don't want the scanning overhead and improve the startup performance, you can register as below:
builder.RegisterGeneric(typeof(ConcreteGenericType<>)).As(typeof(IServiceType<>);

Categories

Resources