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)
{
}
}
Related
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);
}
}
i am using ASP.NET mvc 5 in visual studio 2013. I have i process to implement generic repository and later on UnitOfWork. I have IGenericRepository which has IQueryable one function, because i want to learn and understand so i kept simple as possible. I have GenericRepository class where i am implementing this interface. I got FunctionContext which is inherited from baseContext. The reason i have baseContext so all the dbcontexts can use one path to hit database but same time keep number of table limited to business need.
I got error coming in GenericRepository class under GetAll function, i believe i am not extracting data properly.
many thanks in advanced....
IGernericRepository
public interface IGenericRepository<TEntity> : IDisposable
{
IQueryable<TEntity> GetAll { get; }
}
GenericRepository
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
private FunctionsContext _Context = new FunctionsContext();
public GenericRepository()
{
}
public IQueryable<TEntity> GetAll()
{
/* return _Context.Functions.Select(x => new Functions
{
Function_ID = x.Function_ID,
Title = x.Title,
Hierarchy_level = x.Hierarchy_level
});*/
????????????????????? need help here!
}
public void Dispose()
{
}
}
FunctionsContext
public class FunctionsContext : BaseContext<FunctionsContext>
{
public DbSet<App_Functions> Functions { get; set; }
}
BaseContext
public class BaseContext<TContext> : DbContext where TContext : DbContext
{
static BaseContext()
{
Database.SetInitializer<TContext>(null);
}
protected BaseContext()
: base("name = ApplicationDbConnection")
{ }
}
Functions Table (model)
[Table("Functions")]
public class App_Functions
{
public App_Functions()
{
// this.App_Controllers = new List<App_Controllers>();
}
[Key]
public int Function_ID { get; set; }
[StringLength(50)]
[Required]
public string Title { get; set; }
public int Hierarchy_level { get; set; }
}
Controller class
using (var repository = new GenericRepository<Functions>())
{
foreach(var functions in repository.GetAll)
{
var a7 = functions.Title;
??????
}
}
DbSet<TEntity> implements IQueryable<> so you should be able to just do this:
public IQueryable<TEntity> GetAll()
{
return _Context.Functions;
}
To make it generic, working for all types of TEntity:
public IQueryable<TEntity> GetAll()
{
return _Context.Set<TEntity>();
}
Give it a try and let me know if it works for you.
I don't think the other answer really addresses the ?????? bit.
This does:
public IQueryable<TEntity> GetAll()
{
return _Context.Set<TEntity>();
}
Although, I wouldn't do it as a method. Do it as a read-only property.
public IQueryable<TEntity> Entities
{
get { return _Context.Set<TEntity>(); }
}
// Generic Repository
public class Repository<T> where T : class
{
private readonly DbSet<T> _dbSet;
public Repository(DbSet<T> dbSet)
{
_dbSet = dbSet;
}
public IEnumerable<T> Where(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where);
}
public T FirstOrDefault(Expression<Func<T, bool>> where)
{
return _dbSet.FirstOrDefault(where);
}
// other methods
}
// Unit of Work interface
public interface IUnitOfWork
{
Repository<Product> ProductsRepository { get; }
}
// Unit of Work implementation
public class MyContext : DbContext, IUnitOfWork
{
private readonly Repository<Product> _products = null;
public DbSet<Product> Products { get; set; }
public MyContext()
: base("MyContext")
{
_products = new Repository<Product>();
}
public Repository<Product> ProductsRepository
{
get { return _products; }
}
}
ObjectContext allows generic access to the generated Entities. DbContext appears to have no such support. Accessing EF5 with a generic repository is challenging. Let's say I want a generic mechanism to read any given entity, call it TEntity:
public class DataRepositoryEF5<T> where T: DbContext
{
private ObjectContext _context;
public DataRepositoryEF5(DbContext context)
{
_context = ((IObjectContextAdapter)context).ObjectContext;
}
public IEnumerable<TEntity> ReadAll<TEntity>() where TEntity : class,new()
{
return GetObjectSet<TEntity>().AsEnumerable();
}
protected ObjectSet<TEntity> GetObjectSet<TEntity>() where TEntity : class,new()
{
ObjectSet<TEntity> result;
result = _context.CreateObjectSet<TEntity>();
return result;
}
}
Usage
var context = new MyContext();
var repository = new DataRepositoryEF5<MyContext>(context);
IEnumerable<Document> results = repository.GetAll<Document>();
foreach (var item in results)
{
Console.WriteLine("{0} {1} {2} {3}", item.Description, item.Id, item.Property, item.Whatever);
}
EF used to generate classes with a common base type EntityObject. Since that is no longer the case the best constraint I can have is class...
Second, because there is no equivalent of ObjectContext.CreateObjectSet<>() I am forced to cast from DbSet to ObjectSet.
With this pattern in place I have no need for DbContext. Without generics I am forced to hand code all CRUD operations. Did I miss something? If not, is it possible to tell EF5 to generate code with ObjectContext instead?
have a look at this tip that I posted a while ago
Anyway the trick is by using context.Set<>
public interface IEntity
{
int Id { get; set; }
}
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
{
private IDbContext _context;
public Repository(IDbContext context)
{
_context = context;
}
private IDbSet<TEntity> DbSet
{
get
{
return _context.Set<TEntity>();
}
}
public IQueryable<TEntity> GetAll()
{
return DbSet.AsQueryable();
}
public void Delete(TEntity entity)
{
DbSet.Remove(entity);
}
.....
This is a more up to date and complete answer.
using Library.Models;
using Library.EF;
namespace Abstractions
{
public interface IKeyedModel
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
}
public class GenericContext<T> : DbContext where T : class, IKeyedModel
{
public DbSet<T> Items { get; set; } = default!;
}
public interface IRepository<T> where T : class, IKeyedModel
{
public GenericContext<T> Context() => new GenericContext<T>();
public T Select(int id) => Context().Items.Single(x => x.Id == id);
public IQueryable<T> Select() => Context().Items;
public void Insert(T item)
{
var context = Context();
context.Items.Add(item);
context.SaveChanges();
}
public void Update(T item)
{
var context = Context();
context.Items.Attach(item);
context.SaveChanges();
}
public void Delete(int id)
{
var context = Context();
var item = context.Items.Single(x => x.Id == id);
context.Items.Remove(item);
context.SaveChanges();
}
}
}
namespace Models
{
[Table(name: "User")]
public class User : IKeyedModel
{
public int Id { get; set; }
public string Name { get; set; } = default!;
}
}
namespace App
{
public class UserRepository : IRepository<User> { /* empty */ }
}
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.