Cannot figure out C# generics with multiple and contained generic objects - c#

I am trying to use the template pattern and C# generics to write a utility that will list the entities in the database for any DbSet within Any DbContext. I presume I need three generics:
public class lister<TDbSet, TContext, TEntity>
where TDbSet : DbSet<IPEntity>
where TContext : DbContext
were TEntity : IPEntity
(IPEntity is an abstract base class for all of our entity classes.) Everything seems to be happy except for trying to write a LINQ expression to get the result set. Since "TDbSwt" is actually a MEMBER of TContext, I cannot figure out if LINQ will let you do something like:
from x in TContext.TDbSet select x
It certainly does not like THAT line, whether or not I prefix TDbSet with TContext or not.
Anyone know how I could set this up? right now I have separate (very small, but still one for each entity) class for the entity and LINQ specifics, but as we grow from dozens to hundreds to perhaps thousands of entities, I would like to find a more compact and elegant solution.
Thanks.

from x in TContext.TDbSet select x
There's two reasons this won't work:
TContext is a type, but the member you're trying to call on it isn't static, so you need an instance of TContext
TDbSet is also a type. Just because TContext happens to have a member with the same name (or even the same type) as TDbSet doesn't mean you can start using TDbSet as a member name rather than a type.
What you want is probably something like this:
public class lister<TEntity>
where TEntity : IPEntity
{
private DbContext _context;
private DbSet<TEntity> Set
{
get { return _context.Set<TEntity>(); }
}
public lister(DbContext context)
{
_context = context;
}
}
Now within that class you can write:
from x in Set select x
And it will work as you expected.
Alternatively, you might want lister to itself instantiate the context. This is less likely to be what you should be doing, but I can't be sure without seeing your overall design. In this case, you'd instead want:
public class lister<TEntity, TContext>
where TEntity : IPEntity
where TContext : DbContext, new()
{
private TContext _context;
private DbSet<TEntity> Set
{
get { return _context.Set<TEntity>(); }
}
public lister()
{
_context = new TContext();
}
}
There may be some variation. You may want to pass in or instantiate your context from another method rather than the constructor, for example.

As you can see you cannot use a dynamic name for a member with generics. This would not be statically verifiable (like the C# type system likes to be).
Fortunately, DbContext has a member Set<T>().
public class lister<TDbSet, TEntity>
where TDbSet : DbSet<TEntity>
were TEntity : IPEntity
static DbSet<TEntity> Read(DbContext ctx) { return ctx.Set<TEntity>(); }
As you can see the context does not need to be generic.

Related

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

Entity Framework: context.CreateObjectSet<T> derived entity issue

I am using EF6 ObjectContext generator which means our entities inherit from EntityObject.
I am trying to implement a generic repository for simple CRUD operations but I've got a specific problem when it comes to derived entities. I can't get the right code to handle this generically despite many different attempts!
public DataRepository(ObjectContext context)
{
_context = context;
_objectSet = _context.CreateObjectSet<T>();
Type baseType = GetBaseEntityType();
if (baseType == typeof(T))
_objectSet = _context.CreateObjectSet<T>();
else
// how to create the objectset here?
// I have tried the below but it blows up at runtime with an invalid cast exception
_objectSet = (IObjectSet<T>)Activator.CreateInstance(typeof(ObjectSetProxy<,>).MakeGenericType(typeof(T), baseType), context);
}
I have read through
Entity Framework: ObjectSet and its (generics) variance
but this seems to be targeted at getting an ObjectQuery rather than an ObjectContext. Any help greatly appreciated. :o)
Update: If there aren't any clean solutions for this are there any workarounds people can think of? I considered an auto-generated list of derived entities with a check on the type etc but as it's a generic repo it must ultimately use IObjectSet, so the following lines fail anyway with an invalid cast _objectSet = (IObjectSet) _context.CreateObjectSet();
This might be of interest. I don't know if the behaviour was changed in version 6 though.
I've ended up sorting this out myself. Passing in the base and derived entities (we only ever have one level of inheritance). Here's a flavour of how it looks in case anybody else needs a pointer ....
public class DataRepository : IRepository
where TBaseEntity : EntityObject
where TDerivedEntity : TBaseEntity
{
protected static Ent ctx{get{return DBContextManager.GetDBContext();}}
private ObjectContext _context;
private readonly ObjectSet<TBaseEntity> _objectSet;
public DataRepository(): this(DBContextManager.GetDBContext()){}
public DataRepository(ObjectContext context)
{
_context = context;
_objectSet = _context.CreateObjectSet<TBaseEntity>();
}
private ObjectQuery<TDerivedEntity> TypedObjectSet
{
get
{
return _objectSet.OfType<TDerivedEntity>();
}
}
public IEnumerable<TDerivedEntity> Find(Expression<Func<TDerivedEntity, bool>> predicate)
{
return TypedObjectSet.Where(predicate);
}
public TDerivedEntity Single(Func<TDerivedEntity, bool> predicate)
{
return TypedObjectSet.Single(predicate);
}
// etc etc

InvalidCastException from Entity.DynamicProxies.MyEntityClass to Entity.DbSet`1[MyEntityClass]

I am having difficulty understanding why I am getting an InvalidCastException
Here is the error message that I'm getting:
Unable to cast object of type 'System.Data.Entity.DynamicProxies.Man_58184D79075BC811252680D7866D3D69D0C46FD038D3B123A5E3B102E1FC77A2' to type 'System.Data.Entity.DbSet`1[ConsoleApplication1.Man]'.
Man is the name of the entity class being passed to TEntity
I've marked the location of the error with a comment in the code.
The purpose of this repository class is to hold a given DbContext and DbSet, and call its methods to delete records and/or display all records of the given DbSet.
public class Repository<TEntity> where TEntity : class
{
private DbContext dbContext;
private DbSet<TEntity> dbSet { get; set; }
public Repository(DbContext dbContext)
{
this.dbContext = dbContext;
this.dbSet = this.dbContext.Set<TEntity>();
}
public void Delete(int id)
{
TEntity entity = dbSet.Find(id);
dbSet.Remove(entity);
dbContext.SaveChanges();
}
public void DisplayAll()
{
IQueryable Query = from item in dbContext.Set<TEntity>()
where true
select item;
foreach (DbSet<TEntity> x in Query) //InvalidCastException
{
// print entity fields to console here.
}
}
}
These are the lines in my main program that I use to create a Repository Object:
TestDataBaseEntities is the type of DbContext object I use to refer to my entity model.
class Program
{
static void Main(string[] args)
{
int myID;
var dbEntities = new TestDatabaseEntities();
Repository<Man> ManTracker = new Repository<Man>(dbEntities);
ManTracker.Display(); // exception thown in this method.
Console.WriteLine("Choose ID to delete from Men list");
myID = int.Parse(Console.ReadLine());
ManTracker.Delete(myID);
ManTracker.Display();
Console.ReadLine();
}
}
I think I figured out the answer. I was using the wrong type in the for each statement.
Instead of doing DbSet I should have used TEntity instead.
But this is misleading because even though i'm able to typecast to TEntity, I would need to have code that prints the fields of the TEntity object, and TEntity doesn't have access to the fields I need to print. Instead of using a separate repository class, I think i should use an interface that defines a function called display, and gets implemented by each entity class somehow without modifying the auto-generated code from creating the model from the database. Perhaps by creating classes that inherit from the entity classes, and then implement the interface. I had the thought to do it this way because to keep things object-oriented, it seemed to make sense to me that an object shouldn't rely on another object to display its contents to the user, it should display them itself.

Generics problem with Unit of Work Pattern

I need some help with the design of the Unit of Work + Repository + IoC pattern. I have several interfaces defined as follows:
public interface IRepository<T>
{
T GetEntity(int id);
}
public interface IUserRepository : IRepository<User>
{
User GetUserByXyz(int id);
}
public interface IUnitOfWork
{
T Respository<T>() where T : IRepository<T>;
}
I am using Unity to resolve some references. Here's the implementation of the UoW:
public class UnitOfWork : IUnitOfWork
{
public T Respository<T>() where T : IRepository<T>
{
var container = new UnityContainer();
return container.Resolve<T>();
}
}
Now i am having trouble calling the interface:
User user = _unitOfWork.Respository<IUserRepository>().GetUserByXyz(1);
The type 'IUserRepository' cannot be used as type parameter 'T' in
the generic type or method 'IUnitOfWork.Respository()'. There is no
implicit reference conversion from 'IUserRepository' to
'IRepository'.
How do get around the generic constraint error?
Expanding on my comment:
The statement public T Respository<T>() where T : IRepository<T> implies that you're expecting a type that is a Repository of itself, e.g. IUserRepository would have to be an IRepository<IUserRepository> to satisfy your condition.
You need two different generics, one for the item that is held in the reporsitory TItem and another for the repository itself, TRepo.
Then the whole code becomes:
public interface IRepository<TItem>
{
TItem GetEntity(int id);
}
public interface IUserRepository : IRepository<User>
{
}
public interface IUnitOfWork
{
TRepo Respository<TRepo,TItem>() where TRepo : IRepository<TItm>;
}
and
public class UnitOfWork : IUnitOfWork
{
public TRepo Respository<TRepo,TItem>() where TRepo : IRepository<TItem>
{
var container = new UnityContainer();
return container.Resolve<TRepo>();
}
}
finally, the call becomes:
User user = _unitOfWork.Respository<IUserRepository,User>().GetEntity(1);
Initial note:
_unitOfWork.Respository<IUserRepository>()…
As it is, you're essentially "abusing" UnityOfWork as a service locator (you can ask it for any type of repository), but it doesn't seem to offer any additional benefits. Is this really what you want? Couldn't you just do away with UnitOfWork and do the following instead:
_unityContainer.Resolve<IUserRepository>()…
Alternative solution that does not require a second type parameter:
I agree with #Jon Egerton that for this to work correctly, one option would be to introduce a second generic type parameter (TItem next to TItemRepository). There is, however, another solution involving a marker interface IRepository:
// non-generic marker interface (empty)
public interface IRepository {}
public interface IRepository<T> : IRepository { … /* as before */ }
// ^^^^^^^^^^^^^
// added
public class UnitOfWork
{
public TRepository Get<TRepository>() where TRepository : IRepository
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// this way, no 2nd type parameter is
// needed since the marker interface is
// non-generic.
{
return new UnityContainer().Resolve<TRespository>();
}
}
As requested: Unit of Work example:
If you follow Martin Fowler's definition for the Unit of Work pattern, you get something rather different from what you've got right now. Rather, a Unit of Work according to his udnerstanding merely keeps track of all changes that have been made to a collection of objects. The idea behind this is that changes aren't persisted (e.g. to a database) one at a time, but all at the same time, when requested through the unit of work object; thus the pattern's name:
class UnitOfWork<T>
{
// the UnitOfWork object tracks changes to objects of type T:
private HashSet<T> newItems;
private HashSet<T> modifiedItems;
private HashSet<T> removedItems;
public void Commit()
{
// let's say items are persisted to an RDBMS, then:
// * generate 'DELETE FROM [tableForItemsOfTypeT]' statements
// for all items in the 'removedItems' set;
// * generate 'INSERT INTO [tableForItemsOfTypeT]' statements
// for all items in the 'newItems' set;
// * generate 'UPDATE [tableForItemsOfTypeT]' statements
// for all items in the 'modifiedItems' set.
}
}
Your definition of IUnitOfWork seems a little peculiar, and it seems you've got your generic parameter constraint wrong:
public interface IUnitOfWork
{
T Respository<T>() where T : IRepository<T>;
}
I'd try to get rid of the generic parameter constraint, if possible. For example:
public interface IUnitOfWork<T>
{
IRepository<T> Respository { get; }
}
public class UnitOfWork<T> : IUnitOfWork<T>
{
public IRepository<T> Respository
{
get
{
var container = new UnityContainer();
return container.Resolve<IRepository<T>>();
}
}
}
(Admittedly, I'm not sure whether it's a good idea to constrain a UnitOfWork class to one particular object type by parameterizing it this way. You could in theory also have a UnitOfWork class that implements IUnitOfWork<T> several times, for different T, though that's probably equally unwise. Judge yourself what is most appropriate for your purposes.)
Note that you'd then also have to register your types differently. You could possibly also get rid of IUserRepository this way.
P.S.: Probably, Repository does make more sense if it's a method, and not a property, as shown above. I'd choose based on how costly it is to "get" a repository. If it's expensive, make it a method; if it's a cheap operation, a property might be just fine. If you keep it as a method, I'd rename it to GetRepository to better adhere to the common .NET naming guidelines. Alternative approach:
public interface IUnitOfWork
{
IRepository<T> GetRespository<T>()
}
You are confusing your Generic constraint:
public T Respository<T,U>() where T : IRepository<U>
User user = _unitOfWork.Respository<IUserRepository,User>().GetEntity(1);

Categories

Resources