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

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();
}
}

Related

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();

I`m having troubles trying to Unit Test MVC proj with Repository and Unit of Work patterns

So Im cant get it right and I`m trying from about 5 hours and nothing. If some can help I will be greatfull.
So here is my code
MyContextFile:
public class InventoryManagerContext : IdentityDbContext<ApplicationUser>, IInventoryManagerContext
{
public InventoryManagerContext()
: base("name=InventoryManager")
{
}
public virtual IDbSet<Cloth> Clothes { get; set; }
public static InventoryManagerContext Create()
{
return new InventoryManagerContext();
}
}
My Data file:
public class InventoryManagerData: IInventoryManagerData
{
private readonly DbContext context;
private readonly Dictionary<Type, object> repositories = new Dictionary<Type, object>();
public InventoryManagerData()
: this(new InventoryManagerContext())
{
}
public InventoryManagerData(DbContext context)
{
this.context = context;
}
private IRepository<T> GetRepository<T>() where T : class
{
if (!this.repositories.ContainsKey(typeof(T)))
{
var type = typeof(GenericRepository<T>);
this.repositories.Add(typeof(T), Activator.CreateInstance(type, this.context));
}
return (IRepository<T>)this.repositories[typeof(T)];
}
public int SaveChanges()
{
return this.context.SaveChanges();
}
public void Dispose()
{
this.context.Dispose();
}
public IRepository<Cloth> Clothes
{
get { return this.GetRepository<Cloth>(); }
}
}
and Generic Repository Fail:
public class GenericRepository<T> : IRepository<T> where T : class
{
//public GenericRepository()
// : this(new InventoryManagerContext())
//{
//}
public GenericRepository(DbContext context)
{
if (context == null)
{
throw new ArgumentException("An instance of DbContext is required to use this repository.", "context");
}
this.Context = context;
this.DbSet = this.Context.Set<T>();
}
protected IDbSet<T> DbSet { get; set; }
protected DbContext Context { get; set; }
public virtual IQueryable<T> All()
{
return this.DbSet.AsQueryable();
}
public virtual T GetById(int id)
{
return this.DbSet.Find(id);
}
public virtual void Add(T entity)
{
DbEntityEntry entry = this.Context.Entry(entity);
if (entry.State != EntityState.Detached)
{
entry.State = EntityState.Added;
}
else
{
this.DbSet.Add(entity);
}
}
public virtual void Update(T entity)
{
DbEntityEntry entry = this.Context.Entry(entity);
if (entry.State == EntityState.Detached)
{
this.DbSet.Attach(entity);
}
entry.State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
DbEntityEntry entry = this.Context.Entry(entity);
if (entry.State != EntityState.Deleted)
{
entry.State = EntityState.Deleted;
}
else
{
this.DbSet.Attach(entity);
this.DbSet.Remove(entity);
}
}
public virtual void Delete(int id)
{
var entity = this.GetById(id);
if (entity != null)
{
this.Delete(entity);
}
}
public virtual void Detach(T entity)
{
DbEntityEntry entry = this.Context.Entry(entity);
entry.State = EntityState.Detached;
}
}
So Im trying to make Fake Context and the pass it to the Data class, but without success. in the Test controller Im trying to use MOQ, in a example like this:
[TestMethod]
public void Index_ShouldPass()
{
var mockContext = new Mock<IInventoryManagerContext>();
foreach (var cloth in clothes)
{
mockContext.Object.Clothes.Add(cloth);
}
var mockMenuRepository = new Mock<IRepository<Cloth>>(mockContext.Object);
var mockUnitOfWork = new Mock<InventoryManagerData>(mockContext.Object);
mockUnitOfWork.Setup(e => e.Clothes).Returns(mockMenuRepository.Object);
var menuItems = mockUnitOfWork.Object.Clothes.All();
Assert.AreEqual(4, menuItems.Count());
}
So I have interfaces as well IInventoryManagerContext, IInventoryManagerData and IRepository with all Add, Remove signatures.
For data I have really simple List of for Cloth Objects. and tried to add them o the context with loop. But my latest error is:
Test error:
Message: Test method InventoryManager.Tests.Controllers.HomeControllerTests.Index_ShouldPass threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.
Kind of desperate already..

Generic Repository with EntityFramework

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
}

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);

Generic Repository for SQLite-Net in Xamarin Project

I am wondering if there is a way to write a generic repository for my Xamarin project versus writing a different Repository for each entity in my object. The Xamarin Tasky Pro example have one Repository for the Task entity because that is the only entity it has.
In my own project I have more than one Entity, so my question is how can I make
the following Customer Repository to become generic so that the ProductManager, EmployeeManager, etc can use it. If you know of an example or a blog post please point me to the right direction
namespace App.DataLayer
{
public class CustomerRepository
{
private ProntoDatabase _db = null;
protected static string DbLocation;
protected static CustomerRepository Me;
static CustomerRepository()
{
Me = new CustomerRepository();
}
protected CustomerRepository()
{
//set the db location;
DbLocation = DatabaseFilePath;
//instantiate the database
_db = new ProntoDatabase(DbLocation);
}
public static string DatabaseFilePath
{
get
{
const string sqliteFilename = "CustomerDB.db3";
var libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
var path = Path.Combine(libraryPath, sqliteFilename);
return path;
}
}
// CRUD (Create, Read, Update and Delete) methods
public static Customer GetCustomer(int id)
{
return Me._db.GetItem<Customer>(id);
}
public static IEnumerable<Customer> GetCustomers()
{
return Me._db.GetItems<Customer>();
}
public static int SaveCustomer(Customer item)
{
return Me._db.SaveItem(item);
}
public static int DeleteCustomer(int id)
{
return Me._db.DeleteItem<Customer>(id);
}
}
This is an old question but here is my implementation.
I´m using async connections as they provide better performance in mobile projects. The nuggets I installed are Sqlite.Net-PCL/SQLite.Net.Async-PCL on the Core project and the corresponding nuget on the Android project.
My Repository looks like this:
using System;
using System.Collections.Generic;
using Core.Models;
using SQLite.Net;
using System.Linq;
using SQLite.Net.Async;
using System.Threading.Tasks;
using System.Linq.Expressions;
namespace Core.Managers
{
public interface IRepository<T> where T : class, new()
{
Task<List<T>> Get();
Task<T> Get(int id);
Task<List<T>> Get<TValue>(Expression<Func<T, bool>> predicate = null, Expression<Func<T, TValue>> orderBy = null);
Task<T> Get(Expression<Func<T, bool>> predicate);
AsyncTableQuery<T> AsQueryable();
Task<int> Insert(T entity);
Task<int> Update(T entity);
Task<int> Delete(T entity);
}
public class Repository<T> : IRepository<T> where T : class, new()
{
private SQLiteAsyncConnection db;
public Repository(SQLiteAsyncConnection db)
{
this.db = db;
}
public AsyncTableQuery<T> AsQueryable() =>
db.Table<T>();
public async Task<List<T>> Get() =>
await db.Table<T>().ToListAsync();
public async Task<List<T>> Get<TValue>(Expression<Func<T, bool>> predicate = null, Expression<Func<T, TValue>> orderBy = null)
{
var query = db.Table<T>();
if (predicate != null)
query = query.Where(predicate);
if (orderBy != null)
query = query.OrderBy<TValue>(orderBy);
return await query.ToListAsync();
}
public async Task<T> Get(int id) =>
await db.FindAsync<T>(id);
public async Task<T> Get(Expression<Func<T, bool>> predicate) =>
await db.FindAsync<T>(predicate);
public async Task<int> Insert(T entity) =>
await db.InsertAsync(entity);
public async Task<int> Update(T entity) =>
await db.UpdateAsync(entity);
public async Task<int> Delete(T entity) =>
await db.DeleteAsync(entity);
}
}
Some examples on how to use it:
var connection = new SQLiteAsyncConnection(() => sqlite.GetConnectionWithLock());
await connection.CreateTablesAsync<Ingredient, Stock>();
IRepository<Stock> stockRepo = new Repository<Stock>(connection);
IRepository<Ingredient> ingredientRepo = new Repository<Ingredient>(connection);
var stock1 = new Stock {
IngredientId = 1,
DaysToExpire = 3,
EntryDate = DateTime.Now,
Location = StockLocations.Fridge,
MeasureUnit = MeasureUnits.Liter,
Price = 5.50m,
ProductName = "Leche Auchan",
Quantity = 3,
Picture = "test.jpg",
Family = IngredientFamilies.Dairy
};
var stockId = await stockRepo.Insert(stock1);
var all = await stockRepo.Get();
var single = await stockRepo.Get(72);
var search = await stockRepo.Get(x => x.ProductName.StartsWith("something"));
var orderedSearch = await stockRepo.Get(predicate: x => x.DaysToExpire < 4, orderBy: x => x.EntryDate);
If the Repository does not meet your query needs, you can use AsQueryable():
public async Task<List<Stock>> Search(string searchQuery, StockLocations location, IngredientFamilies family)
{
var query = stockRepo.AsQueryable();
if (!string.IsNullOrEmpty(searchQuery))
{
query = query.Where(x => x.ProductName.Contains(searchQuery) || x.Barcode.StartsWith(searchQuery));
}
if (location != StockLocations.All)
{
query = query.Where(x => x.Location == location);
}
if (family != IngredientFamilies.All)
{
query = query.Where(x => x.Family == family);
}
return await query.OrderBy(x => x.ExpirationDays).ToListAsync();
}
My implementation with the help of unity IOC is given below, My project includes the PCL, Xamarin Android & Xamarin iOS projects
Define a base model with primary key
public class BaseModel
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
}
Define a generic base repository as shown below
public interface IBaseRepository<T> : IDisposable
where T :BaseModel, new()
{
List<T> GetItems();
T GetItem(int id);
int GetItemsCount();
int SaveItem(T item);
int SaveAllItem(IEnumerable<T> items);
}
public class BaseRepository<T> : BaseRepository<T> where T : BaseModel, new()
{
private static readonly object locker = new object();
protected SQLiteConnection DatabaseConnection;
public BaseRepository(string dbPath)
{
DatabaseConnection = new SQLiteConnection(dbPath);
DatabaseConnection.CreateTable<T>();
}
public List<T> GetItems()
{
lock (locker)
{
return DatabaseConnection.Table<T>().ToList();
}
}
public int GetItemsCount()
{
lock (locker)
{
return DatabaseConnection.Table<T>().Count();
}
}
public T GetItem(int id)
{
lock (locker)
{
return DatabaseConnection.Table<T>().Where(i => i.Id == id).FirstOrDefault();
}
}
public int SaveItem(T item)
{
lock (locker)
{
if (item.Id != 0)
{
return DatabaseConnection.Update(item);
}
else
{
return DatabaseConnection.Insert(item);
}
}
}
}
Define two sample classes which are inherited from the BaseModel
public class Entity1 : BaseModel
{
public int ItemName
{
get;
set;
}
}
public class Entity2 : BaseModel
{
public int Description
{
get;
set;
}
}
public static UnityContainer Container { get; private set; }
public static void InitializeUnityContainer()
{
if (Container == null)
Container = new UnityContainer();
}
Register
Container.RegisterInstance<IBaseRepository<Entity1>>(new BaseRepository<Entity1>(DatabasePath));
Container.RegisterInstance<IBaseRepository<Entity2>>(new BaseRepository<Entity2>(DatabasePath));
resolve like this
using (var repo1 = App.Container.Resolve<IBaseRepository<Entity2>>())
{
}

Categories