[Edited: The entities below are generated by Entity-Framework]
I am trying to implement a generic repository. Below are some interfaces that define specialize traits.
namespace AnimeVoter.DataLayer.Repositories
{
internal interface ICanCreate<TEntity>
{
void Create(TEntity entity);
}
internal interface ICanUpdate<TEntity>
{
bool Update(TEntity entity);
}
internal interface ICanDelete<TEntity>
{
bool Delete(TEntity entity);
}
internal interface ICanGetList<TEntity>
{
IEnumerable<TEntity> GetList();
}
internal interface ICanGetById<TEntity>
{
TEntity GetById(int id);
}
}
Now I also have an abstract class that combines the traits like below.
namespace AnimeVoter.DataLayer.Repositories
{
public abstract class CrudRepository<TEntity> :
ICanCreate<TEntity>,
ICanUpdate<TEntity>,
ICanDelete<TEntity>,
ICanGetList<TEntity>,
ICanGetById<TEntity>
{
public abstract void Create(TEntity entity);
public abstract bool Update(TEntity entity);
public abstract bool Delete(TEntity entity);
public abstract IEnumerable<TEntity> GetList();
public abstract TEntity GetById(int id);
}
}
Then I have somewhere like 10-15 concrete classes that uses the abstraction above. I will show only two. I will also limit the discussion to the common method Create().
Below is for the User table in the database:
namespace AnimeVoter.DataLayer.Repositories.Impl
{
public class UserRepository : CrudRepository<User>, IDisposable
{
DbEntities db = new DbEntities();
public override void Create(User entity)
{
db.Users.AddObject(entity);
}
...
And below is for the Title table in the database:
namespace AnimeVoter.DataLayer.Repositories.Impl
{
public class TitleRepository : CrudRepository<Title>, IDisposable
{
DbEntities db = new DbEntities();
public override void Create(Title entity)
{
db.Titles.AddObject(entity);
}
...
So there's the problem! When I am adding a new record to the User table I do db.Users.AddObject(entity). And when adding to the Title table I do db.Titles.AddObject(entity).
I am now wondering how I can refactor this using generics so I can just do something like db<"TableName">.AddObject(entity) or anything to that effect so I can have just one implementation for all the tables instead of having many implementations for each one of them?
The main thing you need to do is create an ObjectSet for the entity and then perform your actions against that ObjectSet instance. Here is a complete example on how to create a generic repository that handles all the CRUD actions.
public class TitleRepository : CrudRepository<Title>
{
public TitleRepository()
: base(new DbEntities())
{
}
}
public abstract class CrudRepository<TEntity> :
ICanCreate<TEntity>,
ICanUpdate<TEntity>,
ICanDelete<TEntity>,
ICanGetList<TEntity>,
ICanGetById<TEntity>
where TEntity : EntityObject
{
private readonly ObjectSet<TEntity> _objectSet;
private readonly string _primaryKey;
protected CrudRepository(ObjectContext context)
{
this._objectSet = context.CreateObjectSet<TEntity>();
this._primaryKey = this.GetPrimaryKeyPropertyName();
}
public void Create(TEntity entity)
{
this._objectSet.AddObject(entity);
this._objectSet.Context.SaveChanges();
}
public bool Update(TEntity entity)
{
if (entity.EntityState == EntityState.Detached)
{
this._objectSet.Attach(entity);
}
this._objectSet.Context.SaveChanges();
return true;
}
public bool Delete(TEntity entity)
{
this._objectSet.DeleteObject(entity);
this._objectSet.Context.SaveChanges();
return true;
}
public IEnumerable<TEntity> GetList()
{
return this._objectSet.ToList();
}
public TEntity GetById(int id)
{
return this._objectSet.Where(this.CreateGetByIdExpression(id)).FirstOrDefault();
}
// Build an Expression that can be used to query an Entity by Id.
private Expression<Func<TEntity, bool>> CreateGetByIdExpression(object id)
{
ParameterExpression e = Expression.Parameter(typeof(TEntity), "e");
PropertyInfo pi = typeof(TEntity).GetProperty(this._primaryKey);
MemberExpression m = Expression.MakeMemberAccess(e, pi);
ConstantExpression c = Expression.Constant(id, id.GetType());
BinaryExpression b = Expression.Equal(m, c);
Expression<Func<TEntity, bool>> lambda = Expression.Lambda<Func<TEntity, bool>>(b, e);
return lambda;
}
// Use the EF metadata to get the primary key property name.
private string GetPrimaryKeyPropertyName()
{
return this._objectSet.Context
.MetadataWorkspace
.GetEntityContainer(this._objectSet.Context.DefaultContainerName, DataSpace.CSpace)
.BaseEntitySets
.First(meta => meta.ElementType == this._objectSet.EntitySet.ElementType)
.ElementType.KeyMembers
.Select(k => k.Name)
.FirstOrDefault();
}
}
To accomplish this you have to define such mappings between entity type and db table. You can consider something like DbContractFactory and inject it in base CrudRepository<TEntity> class, so it will be able to retrieve Table reference/name in runtime based on current entity type TEntity like
dbContractFactory.GetDbContract<TEntity>()
In this way you can separate db-specifics from entities implementation itself by storing all relations in such factory/map.
EDIT: An example
interface IDbContract
{
string TableName { get; }
}
public sealed class DbContractFactory
{
private readonly IDictionary<Type, IDbContract> dbContractMap;
public void RegisterContract<TEntity>(IDbContract)
{
// store in dbContractMap
}
public IDbContract GetDbContract<TEntity>()
{
if (dbContractMap.Contains(typeof(TEntity))
{
// retrieve and return
}
}
}
Just replace the db.Title with CreateObjectSet
I do it something like this:
public class CrudRepository<TEntity> where TEntity : class
{
DbEntities db = new DbEntities();
public override void Create(TEntity entity)
{
db.CreateObjectSet<TEntity>().AddObject(entity);
}
}
EDIT:
Forgot the where...
I have found the answer here, http://geekswithblogs.net/seanfao/archive/2009/12/03/136680.aspx. This is very good because it eliminates having multiple repository objects for each table mapped by EF particularly for mundane operations like CRUD, which is exactly what I was looking for.
Related
This question already has answers here:
Return Generic Type after determining Type Parameter dynamically
(2 answers)
Closed 3 years ago.
I seem to be having trouble finding a way to cast & return an Activator-created instance as its generic type. I have looked at various examples...but cannot seem to find an "exact" example for what I am trying to accomplish.
How do I return the correct type from the GetRepositoryInstance method below?
For instance...
public class GenericRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
}
public class TransactionProvider : ITransactionProvider
{
public void AddPending(ITransaction transaction)
{
// Get the targeted types
var typeEntity = GetTransactionEntityType(transaction.EntityName);
var typePendingEntity = GetTransactionPendingEntityType(transaction.EntityName);
// Get the Repository Instances for each type
var repositoryEntity = GetRepositoryInstance(typeEntity);
var repositoryPendingEntity = GetRepositoryInstance(typePendingEntity);
}
// -----
// HERE...I want to return the generated type as its' IRepository<>...how?
// -----
private IRepository<> GetRepositoryInstance(Type entity)
{
var repositoryType = typeof(GenericRepository<>).MakeGenericType(entity);
var repository = Activator.CreateInstance(repositoryType);
return repository;
}
}
UPDATE:
Of course, any solution still needs to be able to call methods of IRepository
public interface IRepository<TEntity> : IQueryable<TEntity>, IEnumerable<TEntity>, IEnumerable, IQueryable where TEntity : class
{
#region <Methods>
IQueryable<TEntity> AsQueryable();
void Delete(TEntity entity);
void DeleteList(IList<TEntity> entities);
void DeleteAllOnSubmit(IEnumerable<TEntity> entities);
void DeleteOnSubmit(TEntity entity);
void Insert(TEntity entity);
void InsertList(IList<TEntity> entities);
void InsertAllOnSubmit(IEnumerable<TEntity> entities);
void InsertOnSubmit(TEntity entity);
void Update(TEntity entity);
void UpdateOnSubmit(TEntity entity);
#endregion
}
There is no diamond operator in C# yet like in Java.
So you can't access the open generic type underlying to closed types, which prevents generic polymorphism.
You can try using a non generic interface as root where all IRepository Of T ihnerits from IRepository, which is untidy.
private IRepository GetRepositoryInstance(Type entity);
The thing to remember is that IRepository<> is not a super-type of IRepository<SomeEntity>, and it's a type that can never be instantiated or assigned to, and can only be used as an argument to typeof. IRepository<T> defines a set of separate types, unrelated by inheritance, each with its own static variables, and possibly it's own memory layout.
If you want substitutablity across a family of constructed generic types you need to introduce a common supertype (or just use object). EG
using System;
namespace ConsoleApp30
{
public class GenericRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
}
public interface IRepository<TEntity> : IRepository where TEntity : class
{
}
public interface IRepository
{
}
public interface ITransaction
{
}
public interface ITransactionProvider
{
}
public class TransactionProvider : ITransactionProvider
{
public void AddPending(ITransaction transaction)
{
// Get the targeted types
var typeEntity = typeof(string);
var typePendingEntity = typeof(string);
// Get the Repository Instances for each type
var repositoryEntity = GetRepositoryInstance(typeEntity);
var repositoryPendingEntity = GetRepositoryInstance(typePendingEntity);
}
// -----
// HERE...I want to return the generated type as its' IRepository<>...how?
// -----
private IRepository GetRepositoryInstance(Type entity)
{
var repositoryType = typeof(GenericRepository<>).MakeGenericType(entity);
var repository = Activator.CreateInstance(repositoryType);
return (IRepository)repository;
}
}
class Program
{
static void Main(string[] args)
{
var tp = new TransactionProvider();
tp.AddPending(null);
Console.WriteLine("Hello World!");
}
}
}
I'm working on refactoring a persistence layer to use a true generic repository, and want to minimise the number of similar queries being executed on different tables - think things like get by id from table a, b, or c, where the query only differs by the table.
My repository so far looks like this:
public interface IRepository<T>
{
void Insert(T entity);
void Update(T entity);
}
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
/// ctor stuff omitted ...
public void Insert(TEntity entity)
{
_db.Insert<TEntity>(entity);
}
public void Update(TEntity entity)
{
_db.Update<TEntity>(entity);
}
}
public interface IDerivedRepository : IRepository<MyEntity>
{
// defines interface methods not found on the base IRepository
}
public class DerivedRepository : BaseRepository<MyEntity>, IDerivedRepository
{
// implements methods defined on IDerivedRepository, and inherits Insert and Update from BaseRepository
}
This works nicely in that any new repository can inherit the methods defined on the base repo, which are type agnostic in that I can simply send an entity and my ORM (NPoco) manages the insert/update.
I want to extend that to allow generic base definitions for simple get/fetch type methods - get by id or a simple count being obvious examples. At the moment, I implement these in the appropriate repository so end up with multiple repository methods (in separate repositories) calling essentially the same code.
Example below is simplified (_db manages scope etc) but highlights what I'm trying to avoid - the repeated GetById methods where the table and return type differ
public class DerivedRepositoryA : BaseRepository<A>, IDerivedARepository
{
public A GetById(int id) {
return _db.Fetch<A>("select * from TableA where id = #0", id);
}
}
public class DerivedRepositoryB : BaseRepository<B>, IDerivedBRepository
{
public B GetById(int id) {
return _db.Fetch<B>("select * from TableB where id = #0", id);
}
}
public class DerivedRepositoryC : BaseRepository<C>, IDerivedCRepository
{
public C GetById(int id) {
return _db.Fetch<C>("select * from TableC where id = #0", id);
}
}
Is it possible, and how might I go about it?
The below BaseRepository<TEntity> implementation uses the type name as the table name by default, but allows for a custom table name which differs from the type name if required.
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
private readonly string tableName;
public BaseRepository() : this(typeof(TEntity).Name)
{
}
public BaseRepository(string tableName)
{
this.tableName = tableName;
}
public TEntity GetById(int id)
{
return _db.Fetch<TEntity>($"select * from Table{tableName} where id = {id}");
}
}
You don't need the table name, this would work
return _db.Single<TEntity>("where id = #id", id); //Or Fetch
You could do something like this and let NPoco handle the SQL. You can also use this for a Save< T >() or Delete < T >() also
public T GetByID<T>(Int32 ID)
{
try
{
if (ID == 0)
throw (new ArgumentNullException("ID cannot be 0"));
return _db.SingleOrDefaultById<T>(ID);
}
catch { throw; }
}
I have a class that has some methods, two of them (ADD and UPDATE) want to be generic.
Here is my class:
public class CatalogRepository : ICatalogRepository
{
public CatalogRepository(DbContext dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("dbContext");
DbContext = dbContext;
}
private DbContext DbContext { get; set; }
#region Generic ADD and UPDATE
public void Add<T>(T entity) where T : DbSet
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != System.Data.Entity.EntityState.Detached)
{
dbEntityEntry.State = System.Data.Entity.EntityState.Added;
}
else
{
DbContext.Set<T>().Add(entity);
}
}
public void Update<T>(T entity) where T : DbSet
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State == System.Data.Entity.EntityState.Detached)
{
DbContext.Set<T>().Attach(entity);
}
dbEntityEntry.State = System.Data.Entity.EntityState.Modified;
}
#endregion
#region SetupSensor
public IEnumerable<SetupSensor> GetSetupSensors(string masterEntity)
{
return DbContext.Set<SetupSensor>().Where(c => c.MasterEntity == masterEntity).ToList();
}
public IEnumerable<SetupSensor> ReadOnlySetupSensors(string masterEntity)
{
return DbContext.Set<SetupSensor>().AsNoTracking().Where(c => c.MasterEntity == masterEntity).ToList();
}
public SetupSensor GetSetupSensor(int sensorId)
{
return DbContext.Set<SetupSensor>().Where(c => c.SensorId == sensorId).FirstOrDefault();
}
#endregion
}
Here is the Interface Implementation:
public interface ICatalogRepository
{
SetupSensor GetSetupSensor(int sensorId);
IEnumerable<SetupSensor> GetSetupSensors(string masterEntity);
void Add<T>(T entity);
void Update<T>(T entity);
}
When I build I get the following error on the Two Generic Methods:
The constraints for type parameter 'T' of method 'CatalogRepository.Add<T>(T)' must match the constraints for type parameter 'T' of interface method 'ICatalogRepository.Add<T>(T)'. Consider using an explicit interface implementation instead.
Any clue on how to deal with this?
Well, the error is pretty self-explanatory. When implementing an interface, you must implement all its members exactly as they are defined.
Since you've introduced additional generic constraints in the implementation that are not present in the interface, implementation does not match the interface.
There are two ways to fix this: either add the constraints to the interface, or remove them from the implementation.
As a sidenote, you may want to think about making the whole interface generic, that is, to declare it like this:
// you may or may not want to have the constraint here
public interface ICatalogRepository<T> where T : DbSet
{
// sensor methods
void Add(T entity);
void Update(T entity);
}
In your implementation you do this:
public void Add<T>(T entity) where T : DbSet
{ … }
While your interface specifies this:
void Add<T>(T entity);
So, essentially, you need to make the constraints (the where part) identical for both sides. In your case, as you need the DbSet constaint for the implementation, you should add it to the interface:
void Add<T>(T entity) where T : DbSet;
I know that this question seems to be already made here, but I have specific doubts, mainly in database-first usage and lack of code-example in replied questions.
I have these layers: Core, Data and UI (asp.net mvc).
I have these tables in MSSQL: Person and Contact.
Question 1: In data layer, EDMX generates Person and Data POCO. Where I write methods like SearchPersonByCity() ? Do I need to create another Person class in the same data layer, just for writting data CRUD? How I make this? Please make an example (classes, namespaces, etc.. not necessary the whole actual code)
Question 2: How do I transpose these data between data-layer and core (domain models)? Where do I need to create the same SearchPersonByCity() in core (domain) class? Maybe create another Person class in core-layer just for these data-acess methods?
Please give me some code example, and how big companies do in real life, because it seems to be so dumb and a lot of code to mantain and problaby I'm getting something wrong.
I'm not lazy, and I read hundreds pages of Entity Framework books, questions here, and I can't figure out how to do this in code.
In my opinion I would use the repository pattern in your case, so first you have a IRepository class defined:
public interface IRepository<T> where T :
{
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(Expression<Func<T, bool>> where);
T GetById(long id);
T GetById(string id);
T Get(Expression<Func<T, bool>> where);
}
And an abstract base RepositoryBase class:
public abstract class RepositoryBase<T> where T : class
{
private PersonDBEntities dataContext;
private readonly IDbSet<T> dbset;
protected RepositoryBase(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
dbset = DataContext.Set<T>();
}
protected IDatabaseFactory DatabaseFactory
{
get;
private set;
}
protected PersonDBEntities DataContext
{
get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
}
public virtual void Add(T entity)
{
dbset.Add(entity);
}
public virtual void Update(T entity)
{
dbset.Attach(entity);
dataContext.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
dbset.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = dbset.Where<T>(where).AsEnumerable();
foreach (T obj in objects)
dbset.Remove(obj);
}
public virtual T GetById(long id)
{
return dbset.Find(id);
}
public virtual T GetById(string id)
{
return dbset.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return dbset.ToList();
}
//You can return IQueryable if you want to build your expression true later on...
public virtual IEnumerable<T> Get(Expression<Func<T, bool>> where)
{
return dbset.Where(where).ToList();
}
}
And your PersonRepository class:
public class PersonRepository: RepositoryBase<Person>, IPersonRepository
{
public PersonRepository(IDatabaseFactory databaseFactory)
: base(databaseFactory)
{
}
}
public interface IPersonRepository : IRepository<Person> // Person will be your POCO class
{
}
Next step is on your service layer, you will define and implement that actual SearchPersonByCity() method:
public class PersonService : IPersonService
{
private readonly IPersonRepository personRepository;
private readonly IUnitOfWork unitOfWork;
public PersonService(IPersonRepository personRepository, IUnitOfWork unitOfWork)
{
this.personRepository = personRepository;
this.unitOfWork = unitOfWork;
}
public IEnumerable<Person> SearchPersonByCity(string city)
{
var persons = personRepository.Get(p => p.City == city);
return persons;
}
}
Im not sure if what I'd like to do is possible since I haven't found anything on google and after about 30minutes of intensive search I decided to ask directly.
I have definded a simple interface for my repository
public interface IRepository<TEntity> : IDisposable
{
TEntity GetById(object id);
List<TEntity> GetAll();
}
Now I want to implement my first repository and it works like this
public class ContentRepository : IRepository<ContentPages>
{
private readonly Context _db = new Context();
public ContentPages GetById(object id)
{
var result = _db.ContentPages.Find(id);
return result;
}
public List<ContentPages> GetAll()
{
return _db.ContentPages.ToList();
}
public void Dispose()
{
_db.Dispose();
}
}
This works fine but when I inject my repository to my mvc Controller it takes an IRepository<ContentPages> as parameter type and I just want it to take an IRepository.
I tried to move the generic type to the functions itself like this
public interface IRepository : IDisposable
{
TEntity GetById<TEntity>(object id);
List<TEntity> GetAll<TEntity>();
}
}
When I do this I don't know how to define my generic type TEntity in the implementation
So in conclusion I want my use the interface without speficing a type so it gets the type from the actual object like this
public constructor1(IRepository ContentRepository){}
the next controller gets this constructor
public constructor2(IRepository BlogRepository){}
and so on
I hope I could describe my problem close enough for u guys to understand :)
Within the concrete implementation of IRepository Class you can define the type of the TEntity as follows.
public TEntity GetById<TEntity>(object id) where TEntity:class
{
// Implimetation
}
But in here according to repository pattern better to use as follows.
public interface IRepository<TEntity>: IDisposable where TEntity : class
try such variant:
public interface IRepository<TEntity> where TEntity : class
{
TEntity Find(params object[] keyValues);
// ...
}
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
private readonly IDbSet<TEntity> _dbSet;
public Repository(IDbContext context)
{
_dbSet = context.Set<TEntity>();
}
public virtual TEntity Find(params object[] keyValues)
{
return _dbSet.Find(keyValues);
}
// ...
}
Example of usage:
IRepository<ApplicationUser> repository = new Repository<ApplicationUser>(new ApplicationDbContext());
ApplicationUser applicationUser = repository.Find("key");
Also, there is a better solution - you can use pattern UnitOfWork. Check this implementation on codeplex. It is really cool.
Example:
public class DatabasesController : Controller
{
private UnitOfWork _unitOfWork;
private WebContext _context;
public DatabasesController()
{
_context = new WebContext();
_unitOfWork = new UnitOfWork(_context);
}
//
// GET: /Databases/
public ViewResult Index()
{
List<Database> databases =
_unitOfWork
.Repository<Database>()
.Query()
.Include(database => database.FileEntitiesInfo)
.Get()
.ToList();
_unitOfWork.Save();
return View(databases);
}
}