Generic Repository with EntityFramework - c#

I'd like to implement a generic repository pattern using Entity Framework (I know there are many controversial opinions about repositories, but still this is what I need).
The interface I'd like it to have is as follows:
public interface IRepository
{
IQueryable<TEntity> Query<TEntity>()
where TEntity: Entity;
void Save<TEntity>(TEntity entity)
where TEntity : Entity;
void Delete<TEntity>(TEntity entity)
where TEntity : Entity;
}
Entity is a base class that just has an int ID property.
And to use it like this:
IRepository repository = ... // get repository (connects to DB)
int userId = GetCurrentUserId();
if (!repository.Query<User>().Any(u => u.Id == userId)) // performs SELECT query
{ /*return error*/ }
var newOrder = new Order { UserId = userId, Status = "New" }
repository.Save(newOrder); // performs INSERT query
...
newOrder.Status = "Completed";
repository.Save(newOrder); // performs UPDATE query
I'd like to avoid UnitOwWork and just commit all object changes to the DB once Save() or Delete() is called. What I want to do looks really simple, but I haven't found any examples of how to do it using EntityFramework.The closest example I could find is this answer, but it uses UnitOwWork and repository-per-entity, which is more complicated than what I need to do.

1-Create One Interface
interface IMain<T> where T : class
{
List<T> GetAll();
T GetById(int id);
void Add(T entity);
void Edit(T entity);
void Del(int id);
int Savechange();
}
2-Create One Class
public class Main<T> : IMain<T> where T : class
{
public DataContext db;
public void Add(T entity)
{
db.Set<T>().Add(entity);
}
public void Del(int id)
{
var q = GetById(id);
db.Set<T>().Remove(q);
}
public void Edit(T entity)
{
db.Entry<T>(entity).State = EntityState.Modified;
}
public List<T> GetAll()
{
return db.Set<T>().Select(a=>a).ToList();
}
public T GetById(int id)
{
return db.Set<T>().Find(id);
}
public int Savechange()
{
return db.SaveChanges();
}
}
3-Create One Repository With Name YourTable ForExample Student
public class Student : Main<Tbl_Student>
{
public Student()
{
db = new DataContext();
}
}
4-To Your Action Write This Code
Student student=new Student();
student.Del(3);
int a = student.Savechange();

You can do that with expression keyword;
public interface IRepository<TEntity> where TEntity : Entity
{
IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> predicate);
void Save(TEntity entity);
void Delete(TEntity entity);
}
public abstract class EfRepository<T> : IRepository<T> where T : Entity
{
private readonly DbContext _dbContext;
protected readonly DbSet<T> _dbSet;
public EfRepository(YourDbContextContext dbContext)
{
_dbContext = dbContext;
_dbSet = dbContext.Set<T>();
}
public void Delete(T entity)
{
if (entity == null) return;
else
{
DbEntityEntry dbEntityEntry = _dbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Deleted)
{
dbEntityEntry.State = EntityState.Deleted;
}
else
{
_dbSet.Attach(entity);
_dbSet.Remove(entity);
_dbContext.SaveChanges();
}
}
}
public IQueryable<T> Query(Expression<Func<T, bool>> predicate)
{
return _dbSet.Where(predicate);
}
public void Save(T entity)
{
if (entity.Id > 0)
{
_dbSet.Attach(entity);
_dbContext.Entry(entity).State = EntityState.Modified;
_dbContext.SaveChanges();
}
else
{
_dbSet.Add(entity);
_dbContext.SaveChanges();
}
}
}
public class Entity
{
public int Id { get; set; }
}
Then create your repositories;
public interface IUserRepository : IRepository<User>
{
//Also you can add here another methods according to your needs
}
public class UserRepository : EfRepository<User>,IUserRepository
{
public UserRepository(YourDbContext yourDbContext) : base(yourDbContext)
{
}
}
Then use it;
IUserRepository _userRepository => Getit
//If there are entities according to your conditions, this will return them, then use it
_userRepository.Query(u => u.Id == userId);

I used to use it but as many developers have said it will add more complexity to your code and can cause problems:
Code for my interface IRepositoryBase:
public interface IRepositoryBase<TEntity> where TEntity : class
{
void Add(TEntity objModel);
void AddRange(IEnumerable<TEntity> objModel);
TEntity GetId(int id);
Task<TEntity> GetIdAsync(int id);
TEntity Get(Expression<Func<TEntity, bool>> predicate);
Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate);
IEnumerable<TEntity> GetList(Expression<Func<TEntity, bool>> predicate);
Task<IEnumerable<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate);
IEnumerable<TEntity> GetAll();
Task<IEnumerable<TEntity>> GetAllAsync();
int Count();
Task<int> CountAsync();
void Update(TEntity objModel);
void Remove(TEntity objModel);
void Dispose();
}
Code for implementatation of my interface on the repsoitory RepositoryBase:
public class RepositoryBase<TEntity> : IRepositoryBase<TEntity> where TEntity : class
{
#region Fields
protected readonly EntityContext _context = new EntityContext();
#endregion
#region Methods
public void Add(TEntity objModel)
{
_context.Set<TEntity>().Add(objModel);
_context.SaveChanges();
}
public void AddRange(IEnumerable<TEntity> objModel)
{
_context.Set<TEntity>().AddRange(objModel);
_context.SaveChanges();
}
public TEntity GetId(int id)
{
return _context.Set<TEntity>().Find(id);
}
public async Task<TEntity> GetIdAsync(int id)
{
return await _context.Set<TEntity>().FindAsync(id);
}
public TEntity Get(Expression<Func<TEntity, bool>> predicate)
{
return _context.Set<TEntity>().FirstOrDefault(predicate);
}
public async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate)
{
return await _context.Set<TEntity>().FirstOrDefaultAsync(predicate);
}
public IEnumerable<TEntity> GetList(Expression<Func<TEntity, bool>> predicate)
{
return _context.Set<TEntity>().Where<TEntity>(predicate).ToList();
}
public async Task<IEnumerable<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate)
{
return await Task.Run(() =>
_context.Set<TEntity>().Where<TEntity>(predicate));
}
public IEnumerable<TEntity> GetAll()
{
return _context.Set<TEntity>().ToList();
}
public async Task<IEnumerable<TEntity>> GetAllAsync()
{
return await Task.Run(() => _context.Set<TEntity>());
}
public int Count()
{
return _context.Set<TEntity>().Count();
}
public async Task<int> CountAsync()
{
return await _context.Set<TEntity>().CountAsync();
}
public void Update(TEntity objModel)
{
_context.Entry(objModel).State = EntityState.Modified;
_context.SaveChanges();
}
public void Remove(TEntity objModel)
{
_context.Set<TEntity>().Remove(objModel);
_context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
#endregion
}
My entities interface:
public interface IMyEntityRepository : IRepositoryBase<MyEntity>
{
//here you can place other implementations your repository doesn't have
}
public class MyEntityRepository : RepositoryBase<MyEntity>, IMyEntityRepository
{
}
How to call it (I was using dependency injection):
public class MyServiceOrController
{
#region Fields
private readonly IMyEntityRepository _myEntityRepository;
#endregion
#region Constructors
public MyServiceOrController(IMyEntityRepository myEntityRepository)
{
_myEntityRepository = myEntityRepository;
}
#endregion
#region Methods
public IList<MyEntity> TestGetAll()
{
return _myEntityRepository.GetAll();
}
#endregion
}

Related

How can I write the following code in layered architecture and generic?

I am developing a project in layered architecture to improve myself.Now let me show you my layers.
public class EfEntityRepositoryBase<TEntity, TContext> : IEntityRepository<TEntity>
where TEntity : class, IEntity, new()
where TContext : DbContext, new()
{
public void Add(TEntity entity)
{
using (var context = new TContext())
{
var addedEntity = context.Entry(entity);
addedEntity.State = EntityState.Added;
context.SaveChanges();
}
}
public async void AddAsync(TEntity entity)
{
using (var context = new TContext())
{
context.Add(entity);
await context.SaveChangesAsync();
}
}
public void Delete(TEntity entity)
{
using (var context = new TContext())
{
var removedEntity = context.Entry(entity);
removedEntity.State = EntityState.Deleted;
context.SaveChanges();
}
}
public TEntity Get(Expression<Func<TEntity, bool>> filter = null)
{
using (var context = new TContext())
{
return context.Set<TEntity>().SingleOrDefault(filter);
}
}
public List<TEntity> GetList(Expression<Func<TEntity, bool>> filter = null)
{
using (var context = new TContext())
{
return filter == null
? context.Set<TEntity>().ToList()
: context.Set<TEntity>().Where(filter).ToList();
}
}
public void Update(TEntity entity)
{
using (var context = new TContext())
{
var updatedEntity = context.Entry(entity);
updatedEntity.State = EntityState.Modified;
context.SaveChanges();
}
}
}
This is my data access layer.
public class IProductManager : IProductService
{
private IProductDal _productDal;
public IProductManager(IProductDal productDal)
{
_productDal = productDal;
}
public void Add(Product product)
{
_productDal.Add(product);
}
public void AddAsync(Product product)
{
_productDal.AddAsync(product);
}
public void Delete(int productId)
{
_productDal.Delete(new Product { ProductId = productId });
}
public List<Product> GetAll()
{
return _productDal.GetList();
}
public List<Product> GetByCategoryId(int categoryId)
{
return _productDal.GetList(p => p.CategoryId == categoryId || categoryId== 0);
}
public Product GetById(int productId)
{
return _productDal.Get(p => p.ProductId == productId);
}
public void Update(Product product)
{
_productDal.Update(product);
}
}
This is my business layer.
When I try to write generic, some things are missing, so I don't understand much.
Now I want to ask, how should I write the following code, because I'm new, I couldn't do it.
List<Product> products = context.Products.Include(x => x.Photos).ToList();
I can post it anywhere you want.
If I understood you correctly, you want to change
List<Product> products = context.Products.Include(x => x.Photos).ToList()
to something like:
List<T> entities = context.Set<T>().Include(x => x.Photos).ToList()
You can not. Let's say you want to use this generic class for your Category model and your Category model does not have Photos property.
I would suggest to make a generic Repository class for all CRUD operations
public interface IBaseRepository<T> where T : class
{
Task AddAsync(T entity);
void Delete(T entity);
void Update(T entity);
Task<IEnumerable<T>> GetAllAsync();
Task<T> FindAsync(Expression<Func<T, bool>> expression);
}
And implementing:
public class BaseRepository<T> : IBaseRepository<T> where T : class
{
protected readonly AppDbContext _context;
private DbSet<T> _dbSet;
public DbSet<T> DbSet => _dbSet ??= _context.Set<T>();
public BaseRepository(AppDbContext context)
{
_context = context;
}
public async Task AddAsync(T entity)
{
await DbSet.AddAsync(entity);
}
public void Delete(T entity)
{
DbSet.Remove(entity);
}
public void Update(T entity)
{
DbSet.Update(entity);
}
// here we made vitrual, this gaves us opportunity to override this method
public virtual async Task<IEnumerable<T>> GetAllAsync()
{
return await DbSet.ToListAsync();
}
public virtual async Task<T> FindAsync(Expression<Func<T, bool>> expression)
{
return await DbSet.Where(expression).FirstOrDefaultAsync();
}
}
Your Product Repository:
public class ProductRepository : BaseRepository<Product>
{
public ProductRepository(AppDbContext context) : base(context)
{
}
public async override Task<IEnumerable<Product>> GetAllAsync()
{
return await DbSet.Include(p => p.Photos).ToListAsync();
}
}

ASP.NET DI Exception with Generic Repository and multiple Services

I am confused about DI and these dependency containers.
Can't inject my dbcontext and services into my application.
Unhandled exception. System.ArgumentException: Cannot instantiate implementation type 'Server.Logic.Registration.IRegistrationService' for service type 'Server.Logic.Registration.IRegistrationService'.
Stack trace:
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.Populate() in Microsoft.Extensions.DependencyInjection.dll:token 0x600007a+0xea
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory..ctor(IEnumerable`1 descriptors) in Microsoft.Extensions.DependencyInjection.dll:token 0x6000079+0x3e
Multiple guides on the internet are about single IRepository and a single table.
While I my Repository is generic.
IRepository
public interface IRepository<TEntity> where TEntity : class
{
void Create(TEntity item);
TEntity FindById(int id);
IEnumerable<TEntity> Get();
IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> predicate);
void Remove(TEntity item);
void Update(TEntity item);
}
GenericRepository
public class GenericRepository<TEntity> : IRepository<TEntity>, IDisposable where TEntity : class
{
public DbContext _context { get; set; }
public DbSet<TEntity> _dbSet { get; set; }
public GenericRepository(DbContext context)
{
_context = context;
_dbSet = context.Set<TEntity>();
}
public IEnumerable<TEntity> Get()
{
return _dbSet.AsNoTracking().ToList();
}
public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> predicate)
{
return _context.Set<TEntity>().Where(predicate).ToList();
}
public TEntity FindById(int id)
{
return _dbSet.Find(id);
}
public void Create(TEntity item)
{
_dbSet.Add(item);
_context.SaveChanges();
}
public void Update(TEntity item)
{
_context.Entry(item).State = EntityState.Modified;
_context.SaveChanges();
}
public void Remove(TEntity item)
{
_dbSet.Remove(item);
_context.SaveChanges();
}
protected void Dispose(bool disposing)
{
if (disposing)
{
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
In many points of my code, I have to use multiple entites of these Generic Repository.
Because I have around 9 tables. And sometimes I have to take info from more than one table.
Here is my service and it's interface, which is realizing my logic.
public interface IRegistrationService
{
public JsonResult GetAll();
public JsonResult GetById(int id);
}
And my Registration Service
public class RegistrationService : IRegistrationService
{
public IRegistrationService _sender;
private GenericRepository<RegistrationCountByMonth> _repoWithDates { get; set; }
private GenericRepository<RegistrationCountByDevicesAndMonth> _repoWithDataAndDevices { get; set; }
private GenericRepository<DeviceType> _deviceTypes { get; set; }
public RegistrationService(GenericRepository<RegistrationCountByMonth> dates,
GenericRepository<RegistrationCountByDevicesAndMonth> devices,
GenericRepository<DeviceType> deviceTypes, IRegistrationService sender)
{
_repoWithDates = dates;
_repoWithDataAndDevices = devices;
_deviceTypes = deviceTypes;
_sender = sender;
}
public JsonResult GetAll()
{
var crudeInfoByMonth = _repoWithDates.Get();
List<CleanByMonth> infoListToReturn = new List<CleanByMonth>();
foreach (var crudeInfo in crudeInfoByMonth)
{
if (crudeInfo.Month == DateTime.Today.Month)
{
CleanByMonth item = new CleanByMonth
{
year = crudeInfo.Year, month = crudeInfo.Month, registeredUsers = crudeInfo.NumberOfUsers
};
infoListToReturn.Add(item);
}
}
return new JsonResult(infoListToReturn);
}
public JsonResult GetById(int id)
{
int year = MySimpleMath.TakeNDigits(id, 4);
int month = int.Parse((id % 100).ToString().PadLeft(2, '0'));
var registrationByDeviceAndMonth = _repoWithDataAndDevices.Get();
CleanWithBoth returnInfo = new CleanWithBoth();
returnInfo.year = year;
returnInfo.month = (byte)month;
returnInfo.registeredUsers = 0;
List<Provision> specificData = new List<Provision>();
var devices = _deviceTypes.Get();
foreach (var dataSet in registrationByDeviceAndMonth.Where(x => x.Year == year && x.Month == month))
{
Provision info = new Provision();
info.type = devices.First(x => x.DeviceId == dataSet.DeviceType.Value).DeviceName;
info.value = dataSet.NumberOfUsers;
specificData.Add(info);
if (dataSet.NumberOfUsers != null)
{
returnInfo.registeredUsers += dataSet.NumberOfUsers.Value;
}
}
returnInfo.registeredDevices = specificData;
return new JsonResult(returnInfo);
}
}
Here is how I am using the asp.net default dependency injection.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IRegistrationService>();
services.AddTransient<IRegistrationService, RegistrationService>();
services.AddTransient<RegistrationService>();
services.AddScoped(typeof(IRepository<>), typeof(GenericRepository<>));
services.AddDbContext<ApplicationContext>(
options => options.UseSqlServer("name=ConnectionStrings:LocalDB"));
services.AddControllers();
}
I can't understand where is my error. Should I maybe somewhere use "new" ?
Please, can you point me to a solution. I really want to understand DI.
But my projects are not that simple as all of those guides..
Maybe I should move myself to Ninject, instead of the default asp.net DI tools ?
Try removing the following lines:
services.AddTransient();
services.AddTransient();

How can I add relation to my repository pattern with c#?

I have a repository pattern that I am using for an application. Everything is working perfectly today. However, I want to add ability to include relations to other models.
Here is my current IRepository
public interface IRepository<TModel>
where TModel : class
{
// Get records by it's primary key
TModel Get(int id);
// Get all records
IEnumerable<TModel> GetAll();
// Get all records matching a lambda expression
IEnumerable<TModel> Find(Expression<Func<TModel, bool>> predicate);
// Get the a single matching record or null
TModel SingleOrDefault(Expression<Func<TModel, bool>> predicate);
// Add single record
TModel Add(TModel entity);
// Add multiple records
IEnumerable<TModel> AddRange(IEnumerable<TModel> entities);
// Remove records
void Remove(TModel entity);
// remove multiple records
void RemoveRange(IEnumerable<TModel> entities);
}
Here is my Entity implementation
public class EntityRepository<TEntity> : IRepository<TEntity>
where TEntity : class
{
protected readonly DbContext Context;
protected readonly DbSet<TEntity> DbSet;
public EntityRepository(DbContext context)
{
Context = context;
DbSet = context.Set<TEntity>();
}
public TEntity Get(int id)
{
return DbSet.Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return DbSet.ToList();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.Where(predicate);
}
public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.SingleOrDefault(predicate);
}
public TEntity Add(TEntity entity)
{
TEntity record = DbSet.Add(entity);
return record;
}
public IEnumerable<TEntity> AddRange(IEnumerable<TEntity> entities)
{
IEnumerable<TEntity> records = DbSet.AddRange(entities);
return records;
}
public void Remove(TEntity entity)
{
DbSet.Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
DbSet.RemoveRange(entities);
}
Now, I want to add another method to allow me to handle lazy loading.
In another words, I want to be able to do something like this
using(var con = new UnitOfWork())
{
var task = con.Tasks.With(x => x.Owner).GetAll();
}
Here in my Unit of work class
public sealed class UnitOfWork : IUnitOfWork
{
private bool Disposed = false;
private readonly ModuleContext Context;
public ITaskRepository Tasks { get; private set; }
public UnitOfWork(ModuleContext context)
{
Context = context;
Tasks = new TaskRepository(Context);
}
public int Save()
{
return Context.SaveChanges();
}
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (!Disposed && Context != null && disposing)
{
Context.Dispose();
}
Disposed = true;
}
}
Here is my task model
public class Task
{
public string Name { get; set; }
[ForeignKey("Client")]
public int ClientId { get; set; }
[ForeignKey("Owner")]
public int? OwnerId { get; set; }
public virtual Client Client { get; set; }
public virtual User Owner { get; set; }
}
How can I add a way to allow me to include relations to different models?
Add an overload for your methods to the repository interface to accept a list of possible include-expressions. E.g.
public IEnumerable<TEntity> FindAll(params Expression<Func<TEntity,object>>[] includes)
{
var query = DbSet;
foreach (var include in includes)
{
query = query.Include(include);
}
return query.ToList();
}
And then you can just write:
uow.Tasks.GetAll(t=>t.Owner);
For the filtered case you can do something like this:
public IEnumerable<TEntity> Find(Expression<Func<TEntity,bool>> filter, params Expression<Func<TEntity,object>>[] includes)
{
var query = DbSet;
foreach (var include in includes)
{
query = query.Include(include);
}
return query.Where(filter).ToList();
}
And then you can just write:
uow.Tasks.Find(t=>t.Something==2, t=>t.Owner);

How add method get by ID?

I have general class.
I need create method get seingleOrDefault by ID?
How to do this?
public T GetByID(int id)
{
_entities.Set<T>().Single(x => x.);
}
general class:
public abstract class GenericRepository<C, T> : IGenericRepository<T>
where T : class
where C : DbContext, new()
{
private C _entities = new C();
public C Context
{
get { return _entities; }
set { _entities = value; }
}
public virtual IQueryable<T> GetAll()
{
IQueryable<T> query = _entities.Set<T>();
return query;
}
public IQueryable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
IQueryable<T> query = _entities.Set<T>().Where(predicate);
return query;
}
public virtual void Add(T entity)
{
_entities.Set<T>().Add(entity);
}
public virtual void Delete(T entity)
{
_entities.Set<T>().Remove(entity);
}
public virtual void Edit(T entity)
{
_entities.Entry(entity).State = System.Data.EntityState.Modified;
}
public virtual void Save()
{
_entities.SaveChanges();
}
}
If you are using DbContext then method Set<T>() returns DbSet<T> instead of ObjectSet<T>. DbSet<T> has method DbSet<T>.Find(params object[] keyValues) which does exactly what you are trying to implement:
This method uses the primary key value to attempt to find an entity tracked by the
context. If the entity is not in the context then a query will be
executed and evaluated against the data in the data source, and null
is returned if the entity is not found in the context or in the data
source.
Usage:
public T GetByID(int id)
{
return _entities.Set<T>().Find(id);
}
If you want to do this in a generic fashion, you need to add a generic constraint on T.
Something like
public interface IEntity
{
int Id { get; set; }
}
public abstract class GenericRepository<C, T> : IGenericRepository<T>
where T : class, IEntity
where C : DbContext, new()
{
public T GetByID(int id)
{
_entities.Set<T>().SingleOrDefault(x => x.Id == id);
}
...
}

Generic Repository. FindByID

I am playing around with making a nhibernate generic repo but I am unsure how to make a method to return an entity back by ID
public class NhibernateRepo : INhibernateRepo
{
private readonly ISession session;
public NhibernateRepo(ISession session)
{
this.session = session;
}
public void Create<T>(T entity)
{
session.Save(entity);
}
public void CreateOrUpdate<T>(T entity)
{
session.SaveOrUpdate(entity);
}
public void Delete<T>(T entity)
{
session.Delete(entity);
}
public void Update<T>(T entity)
{
session.Update(entity);
}
public T Get<T>(object id)
{
return session.Get<T>(id);
}
public T Load<T>(object id)
{
return session.Load<T>(id);
}
public void ReadOnly(object entity, bool readOnly = true)
{
session.SetReadOnly(entity, readOnly);
}
public void Evict(object entity)
{
session.Evict(entity);
}
public object Merge(object entity)
{
return session.Merge(entity);
}
public IEnumerable<T> FindAll<T>()
{
return session.Query<T>();
}
}
You can define this method in the repository interface:
interface IRepository<TEntity, TId> where TEntity : class {
TEntity FindById(TId id);
...
}
And following in repository implementation:
class Repository<TEntity, TId> : IRepository<TEntity, TId> where TEntity : class{
public TEntity FindById(TId id) {
return _session.Get<TEntity>(id);
}
}
As a side note, generic repository interface is sometimes considered to be a bad practice from Domain Driven Design perspective.

Categories