How can I achieve something like the following example?
public interface IGenericRepository {
int id { get; }
T GetById<T>() where T : class
}
public class GenericRepository : IGenericRepository {
//Some code here
public T GetById<T>(int tid) where T : class
{
return from tbl in dataContext.GetTable<T> where tbl.id == tid select tbl;
}
}
and I would like to use this as follows:
GenericRepository gr = new GenericRepository();
Category cat = gr.GetById<Category>(15);
Of course this code does not work since if I define T : class then the tbl.id part won't work. But of course, there should be a way to realize this.
UPDATE
Considering driis' answer, I am doing the following, but I still can't get this working:
public interface IEntity
{
int id { get; }
}
public interface IGenericRepository : IEntity
{
T GetById<T>(int id);
}
public class GenericRepository : IGenericRepository
{
public T GetById<T>(int id) {
return from tbl in dataContext.GetTable<T> where tbl.id == tid select tbl;
}
}
At this point tbl.id works, but dataContext.GetTable<T> is giving me a error.
You can constrain T to be a type that contains an ID, you will likely want an interface for it:
public interface IEntity
{
int Id { get; }
}
Then declare your generic method as:
public IQueryable<T> GetById<T>(int tid) where T : IEntity
{
return from tbl in dataContext.GetTable<T> where tbl.id == tid select tbl;
}
Of course you will need to implement IEntity for all entities. I am guessing you are using Linq2Sql or similar, in which case you can just make a partial class definition that includes the interface implementation.
Related
For Organization Units, we used the code below to get entities in an Organization Unit including its child Organization Units. Is it reasonable and how to extend the IRepository to add this feature to all entities?
public virtual List<Product> GetProductsInOuIncludingChildren(long organizationUnitId)
{
var code = _organizationUnitRepository.Get(organizationUnitId).Code;
var query =
from product in _productRepository.GetAll()
join organizationUnit in _organizationUnitRepository.GetAll() on product.OrganizationUnitId equals organizationUnit.Id
where organizationUnit.Code.StartsWith(code)
select product;
return query.ToList();
}
First, inherit IMustHaveOrganizationUnit:
public class Product : Entity, IMustHaveOrganizationUnit
{
public long OrganizationUnitId { get; set; }
}
Then define the extension method:
public static class RepositoryExtensions
{
public static List<TEntity> GetAllInOuIncludingChildren<TEntity, TPrimaryKey>(
this IRepository<TEntity, TPrimaryKey> repository,
long organizationUnitId
)
where TEntity : class, IEntity<TPrimaryKey>, IMustHaveOrganizationUnit
{
using (var organizationUnitRepository = repository.GetIocResolver().ResolveAsDisposable<IRepository<OrganizationUnit, long>>())
{
var code = organizationUnitRepository.Object.Get(organizationUnitId).Code;
var query =
from entity in repository.GetAll()
join organizationUnit in organizationUnitRepository.Object.GetAll() on entity.OrganizationUnitId equals organizationUnit.Id
where organizationUnit.Code.StartsWith(code)
select entity;
return query.ToList();
}
}
}
Usage:
var products = _productRepository.GetAllInOuIncludingChildren(organizationUnitId);
You can use the below code.
Interface:
public interface ITestRepository : IRepository<Test, int>
{
}
Class:
public class TestRepository : YourProjectNameRepositoryBase<Test, int>, ITestRepository
{
public TestRepository(IDbContextProvider<YourProjectNameDbContext> dbContextProvider, IObjectMapper objectMapper)
: base(dbContextProvider, objectMapper)
{
}
}
Usage:
public class TestAppService : YouProjectNameAppServiceBase, ITestAppService
{
private readonly ITestRepository _testRepository;
public TestAppService(ITestRepository testRepository,
}
Update
In the latest version, there's no need to pass objectmapper and you need to use the updated constructor below:
public class TestRepository : YourProjectNameRepositoryBase<Test, int>, ITestRepository
{
public TestRepository(IDbContextProvider<YourProjectNameDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}
I've built a generic repository interface off which hang a number of entity specific repository interfaces. Mirroring this structure are a number of concrete repository classes.
I'm getting the following error in my base Repository class:
Type 'TEntity' doesn't match expected type '???'.
Method 'GetAll' cannot implement method from interface '...IRepository<TEntity, TIdentity>'. Return type should be 'System.Collections.Generic.List<???>'.
The minified interface/class structure is as follows:
IRepository:
public interface IRepository<TEntity, in TIdentity> where TEntity : IEntity<TIdentity>
{
...
List<TEntity> GetAll();
...
}
Repository:
internal class Repository<TEntity, TIdentity> : IRepository<TEntity, TIdentity>
where TEntity : class, IEntity<TIdentity>
{
...
protected DbSet<TEntity> Set => _set ?? (_set = _context.Set<TEntity>());
public List<TEntity> GetAll()
{
return Set.ToList();
}
...
}
IRoleRepository:
public interface IRoleRepository : IRepository<Role, Guid>
{
...
Role FindByName(string roleName);
...
}
RoleRepository:
internal class RoleRepository : Repository<Role, Guid>, IRoleRepository
{
...
public Role FindByName(string roleName)
{
return Set.FirstOrDefault(x => x.Name == roleName);
}
...
}
This has a knock on effect in my consuming classes where doing a RoleRepository.GetAll() returns a List<???> rather than a List<Role> as I expected.
Edit - Entity definitions...
IEntity:
public interface IEntity<T>
{
T Id { get; set; }
byte[] Version { get; set; }
}
Entity:
public abstract class Entity<T> : IEntity<T>
{
public T Id { get; set; }
public byte[] Version { get; set; }
}
Role:
public class Role : Entity<Guid>
{
private ICollection<User> _users;
public string Name { get; set; }
public ICollection<User> Users
{
get { return _users ?? (_users = new List<User>()); }
set { _users = value; }
}
}
It looks like your TEntity class is not implementing that interface: IEntity<TIdentity>
Depends if your are using CodeFirst at EntityFramework or ModelFirst you have other options to go with:
ModelFirst: you can use Model.tt file to specify implementation of interfaces you need.
CodeFirst: just implement that interface in the model class.
Third option is just get rid of that where TEntity : IEntity<TIdentity> only if your are not using anything from that interface of course.
I'm following this site: http://deviq.com/repository-pattern/
Which has an example of a repository pattern using a DB context. I'm trying to implement this generic Repository class with a list (I want the Repository class. That's a requirement of mine). However, I'm having issues with the Find method.
public class Repository<T> : IRepository<T> where T : class
{
private List<T> context;
virtual public T Find(int id)
{
// I can't figure out a way to make this work with the list of a generic type
}
}
Is this even possible to make a predicate in the List.Find() with just an ID parameter? I'm guessing no, but what are options?
Another option, if you can't control the type of T so as to apply an interface, is to force your implementers to do the hard work.
public abstract class Repository<T> : IRepository<T> where T : class
{
private List<T> context;
public virtual public T Find(int id)
{
return context.FirstOrDefault(x => GetId(x) == id);
}
public abstract int GetId(T entity);
}
an example implementation may be
// entity
public class Stooge
{
public Stooges MoronInQuestion {get;set;}
public double MoeEnragementFactor {get;set;}
public void PloinkEyes() { /*snip*/ }
public void Slap() { /*snip*/ }
public void Punch() { /*snip*/ }
// etc
}
// enum for an Id? It's not that crazy, sometimes
public enum Stooges
{
Moe = 1,
Larry = 2,
Curly = 3,
Shemp = 4,
Joe = 5,
/* nobody likes Joe DeRita */
//CurlyJoe = -1,
}
// implementation
public class StoogeRepository : IRepository<Stooge>
{
public override int GetId(Stooge entity)
{
if(entity == null)
throw new WOOWOOWOOException();
return (int)entity.MoronInQuestion;
}
}
You can declare that T has an Id property with something like this:
public interface IEntity
{
int Id { get; }
}
public class Repository<T> : IRepository<T> where T : class, IEntity
{
private List<T> context;
virtual public T Find(int id)
{
return context.SingleOrDefault(p => p.Id == id);
}
}
Is it possible to create generic restriction in C# using where to select only classes, who have Field with some name.
for example, I have AbstractService<T>
and I have a method IEnumerable<T> ProvideData(userId);
inside provide data I should select only instances with the same user bla-bla-bla.Where(d => d.UserId == userId). But d.UserId could not be resolved. How it possible to resolve this?
IMPORTANT: I can't inherit T from class or interface, which have UserID field.
An interface is what your are looking for:
public interface IWithSomeField
{
int UserId { get; set; }
}
public class SomeGenericClasss<T>
: where T : IWithSomeField
{
}
public class ClassA : IWithSomeField // Can be used in SomeGenericClass
{
int UserId { get; set; }
}
public class ClassB // Can't be used in SomeGenericClass
{
}
[Edit] As you edited your question to state you cannot change class to implement an interface, here is some alternatives, but none relies on generic constraint :
Check the type in the constructor :
code :
public class SomeClass<T>{
public SomeClass<T>()
{
var tType = typeof(T);
if(tType.GetProperty("UserId") == null) throw new InvalidOperationException();
}
}
Use code contract invariant (not sure about the syntax) :
code :
public class SomeClass<T>{
[ContractInvariantMethod]
private void THaveUserID()
{
Contract.Invariant(typeof(T).GetProperty("UserId") != null);
}
}
Extend existing classes with partial classes
If your source classes are generated, you can cheat. I used this technique with lots of Web References having the same kind of parameter objects
Imagine the Web references produced this proxy code :
namespace WebServiceA {
public class ClassA {
public int UserId { get; set; }
}
}
namespace WebServiceB {
public partial class ClassB {
public int UserId { get; set; }
}
}
You can wrap them using in your own code:
public interface IWithUserId
{
public int UserId { get; set; }
}
public partial class ClassA : IWithUserId
{
}
public partial class ClassB : IWithUserId
{
}
then, for your service, you can instantiate AbstractService for any of the Class of the several web services :
public class AbstractService<T> where T : IWithUserId
{
}
This technique works great but only applies when you can extend class in the same project because of the partial keyword trick.
How can I achieve something like the following?
public interface IGenericRepository
{
int id { get; }
T GetById<T>() where T : class
}
public class GenericRepository : IGenericRepository
{
//Some code here
public T GetById<T>(int tid) where T : class
{
return from tbl in dataContext.GetTable<T> where tbl.id == tid select tbl;
}
}
And I would like to use this as follows:
GenericRepository gr = new GenericRepository();
Category cat = gr.GetById<Category>(15);
Of course, in this usage, tbl.id in the GenericRepository gives me an error.
SOLUTION
public class IHasId
{
public int id { get; set; }
}
public interface IGenericRepository
{
int id { get; }
T GetById<T>(int id) where T : IHasId;
}
public class GenericRepository : IGenericRepository
{
public int id
{
get { throw new NotImplementedException(); }
}
public T GetById<T>(int id) where T : IHasId
{
return from tbl in dataContext.GetTable<T> where tbl.id == tid select tbl;
}
}
And apart from these, DON'T forget to define this somewhere in your model:
public partial class Category : IHasId { }
And the usage is:
Repository rep = new Repository();
Category cat = rep.GetById<Category>(15);
There's a few problems here - the first is that the generic type you're matching is a class, but a class doesn't have a property called 'id'. You need to have your Category class implement an interface that exposes an 'id' property:
public interface IIdentity
{
int identity { get; set; }
}
public class Category : IIdentity
{
public int identity{ get; set; }
}
I don't know why you've exposed 'id' as a property on the IGenericRepository interface - surely this is supposed to be a parameter passed to the find method (as indicated by your implementation). You also need to change the restriction on the 'GetById' method from:
where T : class
to something like
where T : IIdentity
using the interface I've suggested above.
public class IHasId
{
public int id { get; set; }
}
public interface IGenericRepository<T>
{
int id { get; }
T GetById(int id);
}
public class GenericRepository<T> : IGenericRepository<T> where T : IHasId
{
public int id
{
get { throw new NotImplementedException(); }
}
public T GetById(int id)
{
return from tbl in dataContext.GetTable<T> where tbl.id == tid select tbl;
}
}
You get this error because you accept every class where T : class. A class don't have that property.
Create an abstract class or interface to make sure that this property exists and change where T : class to where T : IHasIdProperty.