Generic Repository for SQLite-Net in Xamarin Project - c#

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

Related

How to implement basic CRUD features in one place using ASP.NET Core WebAPI

Consider that I have 3 entities:
User
Blog
News
Each of them has these features:
Read by ID
Read by page
Read by page with sorting
Update with JSON Patch
Delete by ID
Delete entities created before...
The codes will be quite simple and duplicated, do I really have to implement these codes in every controller? I don't want to write code like:
[HttpGet]
public ActionResult<List<Blog>> GetAll()
{
return _context.Blogs.ToList();
}
once and once again.
Following code will help you a lot. Basically you have to use Generic Repository pattern.
public interface IRepository<T> where T : class
{
Task Add(T entity);
Task Add(IEnumerable<T> entities);
void Update(T entity);
void Update(IEnumerable<T> entities);
void Delete(T entity);
void Delete(IEnumerable<T> entities);
Task<int> TotalItems(Expression<Func<T, bool>> filter = null, string includeProperties = "");
Task<IList<T>> GetAll(
int pageNumber = 1,
int pageSize = 8,
bool isPaged = true,
Expression<Func<T, bool>> serachFilter = null,
Expression<Func<T, bool>> filter = null,
Expression<Func<T, T>> select = null,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
string includeProperties = "");
Task<T> GetById(string Id);
Task<IList<TType>> Select<TType>(Expression<Func<T, TType>> select = null) where TType : class;
}
public class Repository<T> : IRepository<T> where T : class
{
private readonly IHttpContextAccessor httpContextAccessor;
DbSet<T> DbSet { get; set; }
public Repository(TestStudyContext context, IHttpContextAccessor httpContextAccessor)
{
DbSet = context.Set<T>();
this.httpContextAccessor = httpContextAccessor;
}
public async Task Add(T entity) => await DbSet.AddAsync(AssignDefaultValueForAdd(entity: entity));
public async Task Add(IEnumerable<T> entities) => await DbSet.AddRangeAsync(AssignDefaultValueForAdd(entities));
public void Delete(T entity) => DbSet.Remove(entity: entity);
public void Delete(IEnumerable<T> entities) => DbSet.RemoveRange(entities: entities);
public async Task<T> GetById(string Id) => await DbSet.FindAsync(Id);
public async Task<int> TotalItems(Expression<Func<T, bool>> filter = null, string includeProperties = "")
{
IQueryable<T> query = DbSet;
foreach (var inlcudeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(inlcudeProperty);
}
if(filter != null)
return await query.CountAsync(filter);
return await query.CountAsync();
}
public void Update(T entity) => DbSet.Update(AssignDefaultValueForUpdate(AssignDefaultValueForAdd(entity: entity)));
public void Update(IEnumerable<T> entities) => DbSet.UpdateRange(AssignDefaultValueForUpdate(entities: entities));
public async Task<IList<TType>> Select<TType>(Expression<Func<T, TType>> select = null) where TType : class
{
IQueryable<T> query = DbSet;
return await query.Select(select).ToListAsync();
}
public async Task<IList<T>> GetAll(
int pageNumber = 1,
int pageSize = 8,
bool isPaged = true,
Expression<Func<T, bool>> serachFilter = null,
Expression<Func<T, bool>> filter = null,
Expression<Func<T, T>> select = null,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
string includeProperties = "")
{
IQueryable<T> query = DbSet;
foreach (var inlcudeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
query = query.Include(inlcudeProperty);
if (filter != null)
query = query.Where(filter);
if (serachFilter != null)
query = query.Where(serachFilter);
if (orderBy != null)
query = orderBy(query);
if(select != null)
query = query.Select(select);
if(isPaged == true)
return await query.AsNoTracking().Skip(pageSize * (pageNumber - 1)).Take(pageSize).ToListAsync<T>();
return await query.AsNoTracking().ToListAsync<T>();
}
#region Helper
private T AssignDefaultValueForUpdate(T entity)
{
Type t = entity.GetType();
var UpdatedOnInfo = t.GetProperty("UpdatedOn");
if (UpdatedOnInfo != null)
UpdatedOnInfo.SetValue(entity, DateTime.Now);
return entity;
}
private IEnumerable<T> AssignDefaultValueForUpdate(IEnumerable<T> entities)
{
var updatedEntities = new List<T>();
foreach (var entity in entities)
updatedEntities.Add(AssignDefaultValueForUpdate(entity));
return updatedEntities;
}
private T AssignDefaultValueForAdd(T entity)
{
Type t = entity.GetType();
var userInfo = t.GetProperty("UserId");
if (userInfo != null)
if (httpContextAccessor.HttpContext.User != null)
{
var userId = httpContextAccessor.HttpContext.User.FindFirst("UserId")?.Value;
userInfo.SetValue(entity, userId);
}
var createdOnInfo = t.GetProperty("CreatedOn");
if (createdOnInfo != null)
createdOnInfo.SetValue(entity, DateTime.Now);
var UpdatedOnInfo = t.GetProperty("UpdatedOn");
if (UpdatedOnInfo != null)
UpdatedOnInfo.SetValue(entity, DateTime.Now);
return entity;
}
private IEnumerable<T> AssignDefaultValueForAdd(IEnumerable<T> entities)
{
var updatedEntities = new List<T>();
foreach (var entity in entities)
updatedEntities.Add(AssignDefaultValueForAdd(entity));
return updatedEntities;
}
#endregion
}
Add following Line (Add Service) into the Startup.cs
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
services.AddScoped(typeof(IUnitOfWork<>), typeof(UnitOfWork<>));
In Controller Need to add following things for CRUD Opertion.
public class CategoriesController : Controller
{
private readonly IUnitOfWork<Category> categoryContext;
public CategoriesController(IUnitOfWork<Category> categoryContext)
{
this.categoryContext = categoryContext;
}
[HttpGet(ApiRoutes.Categories.GetAll)]
public async Task<IList<CategoryViewModel>> GetAll()
{
var list = await categoryContext.Repository.GetAll(isPaged: false);
return list;
}
[HttpPost(ApiRoutes.Categories.CreateAndUpdate)]
public async Task<IList<Category>> CreateAndUpdate([FromBody] Category model)
{
if (string.IsNullOrEmpty(model.Id))
{
model.Id = Guid.NewGuid().ToString();
await categoryContext.Repository.Add(model);
}
else
{
categoryContext.Repository.Update(model);
}
await categoryContext.Commit();
return await GetAll();
}
[HttpDelete(ApiRoutes.Categories.Delete)]
public async Task<IList<Category>> Delete(string id)
{
var category = await categoryContext.Repository.GetById(id);
categoryContext.Repository.Delete(category);
await categoryContext.Commit();
return await GetAll();
}
}
Don't forgot to Use Unit of Work Pattern
public interface IUnitOfWork<T> : IDisposable where T : class
{
IRepository<T> Repository { get; }
Task Commit();
}
public class UnitOfWork<T> : IUnitOfWork<T> where T : class
{
private readonly TestStudyContext context;
public IRepository<T> Repository { get; }
public UnitOfWork(
TestStudyContext context,
IHttpContextAccessor httpContextAccessor)
{
this.context = context;
this.Repository = new Repository<T>(context, httpContextAccessor);
}
public async Task Commit() => await context.SaveChangesAsync();
private readonly bool Disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.Disposed)
if (disposing)
context.Dispose();
}
}

Repository pattern - EF Core returns null to foreign keys

I have this repository implementation:
using mediere_API.DataLayer.Entities;
using mediere_API.DataLayer.Repository.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace mediere_API.DataLayer.Repository.Implementations
{
public class RepositoryBase<T> : IDisposable, IRepositoryBase<T> where T : BaseEntity, new()
{
private readonly DbContext _db;
private readonly DbSet<T> _dbSet;
protected RepositoryBase(EfDbContext context)
{
_db = context;
_dbSet = context.Set<T>();
}
public async Task<IList<T>> GetAll(bool asNoTracking = false, bool includeDeleted = false)
{
var query = includeDeleted
? _dbSet
: _dbSet.Where(entity => entity.DeletedAt == null);
return asNoTracking
? await query.AsNoTracking().ToListAsync()
: await query.ToListAsync();
}
public async Task<T> GetById(int id, bool asNoTracking = false, bool includeDeleted = false)
{
return includeDeleted
? await GetRecords(asNoTracking).FirstOrDefaultAsync(e => e.Id == id)
: await GetRecords(asNoTracking).FirstOrDefaultAsync(e => e.Id == id && e.DeletedAt == null);
}
public virtual void Insert(T record)
{
if (_db.Entry(record).State == EntityState.Detached)
{
_db.Attach(record);
_db.Entry(record).State = EntityState.Added;
}
}
public virtual void Update(T record)
{
if (_db.Entry(record).State == EntityState.Detached)
_db.Attach(record);
_db.Entry(record).State = EntityState.Modified;
}
public void Delete(T record)
{
if (record != null)
{
record.DeletedAt = DateTime.UtcNow;
Update(record);
}
}
public void Dispose()
{
_db?.Dispose();
}
protected IQueryable<T> GetRecords(bool asNoTracking = false, bool includeDeleted = false)
{
var result = includeDeleted ?
_dbSet
:
_dbSet.Where(e => e.DeletedAt == null);
return asNoTracking ? result.AsNoTracking() : result;
}
}
}
Let's say I have this Entity
using mediere_API.Dtos;
using System.ComponentModel.DataAnnotations;
namespace mediere_API.DataLayer.Entities
{
public class Localitate : BaseEntity
{
[Required]
public string Nume { get; set; }
[Required]
public int JudetId { get; set; }
public virtual Judet judet { get; set; }
public Localitate() { }
}
}
I observed that I don't get the foreign keys in my objects.
In order to get all the objects with their FKs, I decided to add this this method in the repository for that collection:
public async Task<List<Localitate>> GetLocalitatiBasicAsync()
{
return await base.GetRecords(true).Select(l => new Localitate()
{
Id = l.Id,
Nume = l.Nume,
judet = l.judet
}).ToListAsync();
}
Is this the best solution? And what would be the difference between using this select with judet too vs using the .include() method to include judet?
Thanks.

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

Get specific columns in UnitOfWork and generic repository in Entity Framework

I have a Windows Forms application and generic Entity Framework (latest version) method and multi-layer design pattern.
I want load specific columns, not all columns.
For example, I have 10 columns (a1...a10), but I want get data from (a1 ... a8) columns only. This is my code for get all columns - how do I that?
Repository layer:
public Repository(GlobalERPEntities context)
{
Context = context;
dbSet = Context.Set<T>();
}
public virtual IEnumerable<T> GetAll()
{
return dbSet.ToList();
}
UnitOfWork layer: call get all method from repository layer
public UnitOfWork()
{
Context = new GlobalERPEntities();
}
public Repository<T> Repository<T>() where T : class
{
if (repositories == null)
{
repositories = new Dictionary<Type, object>();
}
if (repositories.Keys.Contains(typeof(T)) == true)
{
return repositories[typeof(T)] as Repository<T>;
}
Repository<T> repo = new Repository<T>(Context);
repositories.Add(typeof(T), repo);
return repo;
}
BLL layer: call get all method from UnitOfWork layer
protected UnitOfWork uow;
public Service()
{
uow = new UnitOfWork();
}
public virtual IEnumerable<T> GetAll()
{
return uow.Repository<T>().GetAll().ToList();
}
How to change it to get a custom set of columns, and how to call it in my form?
I am giving you a fool-proof solution as follows: first write IUnitOfWork as follows:
public interface IUnitOfWork
{
IRepository<T> Repository<T>() where T : class;
Task SaveChangesAsync();
void ResetContextState();
}
Then UnitOfWork class as follows:
public class UnitOfWork : IUnitOfWork
{
private readonly YourDbContext _dbContext;
private Hashtable _repositories;
public UnitOfWork(YourDbContext dbContext)
{
_dbContext = dbContext;
}
public IRepository<T> Repository<T>() where T : class
{
if (_repositories == null)
_repositories = new Hashtable();
var type = typeof(T).Name;
if (!_repositories.ContainsKey(type))
{
var repositoryType = typeof(Repository<>);
var repositoryInstance =
Activator.CreateInstance(repositoryType
.MakeGenericType(typeof(T)), _dbContext);
_repositories.Add(type, repositoryInstance);
}
return (IRepository<T>)_repositories[type];
}
public async Task SaveChangesAsync()
{
await _dbContext.SaveChangesAsync();
}
public void ResetContextState()
{
_dbContext.ChangeTracker.Entries().Where(e => e.Entity != null).ToList()
.ForEach(e => e.State = EntityState.Detached);
}
}
Now write IRepository interface as follows:
public interface IRepository<TEntity> where TEntity : class
{
IQueryable<TEntity> GetEntities(Expression<Func<TEntity, bool>> condition = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "");
Task<bool> IsEntityExists(Expression<Func<TEntity, bool>> filter = null);
void InsertEntity(TEntity entity);
void InsertEntities(List<TEntity> entities);
void UpdateEntity(TEntity entity, params string[] excludeProperties);
void DeleteEntity(TEntity entity);
void DeleteEntities(List<TEntity> entities);
Task<bool> IsTableEmptyAsync();
}
Then your Repository class as follows:
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
private readonly YourDbContext _dbContext;
private readonly DbSet<TEntity> _dbSet;
public Repository(YourDbContext dbContext)
{
_dbContext = dbContext;
_dbSet = _dbContext.Set<TEntity>();
}
public IQueryable<TEntity> GetEntities(Expression<Func<TEntity, bool>> condition = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = _dbSet;
if (condition != null)
{
query = query.Where(condition);
}
foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
query = orderBy(query);
}
return query;
}
public async Task<bool> IsEntityExists(Expression<Func<TEntity, bool>> condition)
{
bool status = false;
if (condition != null)
{
status = await _dbSet.AnyAsync(condition);
}
return status;
}
public void InsertEntity(TEntity entity)
{
_dbSet.Add(entity);
}
public void InsertEntities(List<TEntity> entities)
{
_dbSet.AddRange(entities);
}
public void UpdateEntity(TEntity entity, params string[] excludeProperties)
{
_dbContext.Entry(entity).State = EntityState.Modified;
foreach (string property in excludeProperties)
{
_dbContext.Entry(entity).Property(property).IsModified = false;
}
}
public void DeleteEntity(TEntity entity)
{
_dbSet.Remove(entity);
}
public void DeleteEntities(List<TEntity> entities)
{
_dbSet.RemoveRange(entities);
}
public async Task<bool> IsTableEmptyAsync()
{
bool hasAny = await _dbSet.AnyAsync();
return !hasAny;
}
}
Then use UnitOfWork in your service class or anywhere as follows:
public class EmployeeService
{
private readonly UnitOfWork _unitOfWork;
public EmployeeService()
{
_unitOfWork = new UnitOfWork();
}
public List<Employee> GetAllEmployees()
{
return _unitOfWork.Repository<Employee>().GetEntities().ToList();
}
public List<string> GetAllEmployeeNames()
{
return _unitOfWork.Repository<Employee>().GetEntities().Select(emp => emp.Name).ToList();
}
}
Better use with dependency injection as follows:
public class EmployeeService
{
private readonly IUnitOfWork _unitOfWork;
public EmployeeService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public List<Employee> GetAllEmployees()
{
return _unitOfWork.Repository<Employee>().GetEntities().ToList();
}
public List<string> GetAllEmployeeNames()
{
return _unitOfWork.Repository<Employee>().GetEntities().Select(emp => emp.Name).ToList();
}
}
When I use the UnitOfWork pattern, I follow pretty much the same template as specified on the Microsoft docs.
You could adjust the Get method (or add a new method) as follows (notice the additional select parameter that we use to specify the columns):
public virtual IEnumerable<TDest> Get<TDest>(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "",
Expression<Func<TEntity, TDest>> select = null)
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
if (select == null)
return (IEnumerable<TDest>)orderBy(query).ToList();
return orderBy(query).Select(select).ToList();
}
else
{
if (select == null)
(IEnumerable<TDest>)query.ToList();
return query.Select(select).ToList();
}
}
Example use
public class Cat
{
public string Name { get; set; }
public int Age { get; set; }
public string { get; set; }
}
// Select Name column only
var names = unitOfWork.CatRepository.Get(
null, // No filter
x => x.OrderByDescending(y => y.Age), // Order by oldest
string.Empty, // No includeProperties
x => x.Name // Select the Name column only
)
// Output: "Fluffy"
// Select Name and Age columns only
var namesAndAges = unitOfWork.CatRepository.Get(
x => x.Age > 1, // Where Age is greater than 1
null, // No order
string.Empty, // No includeProperties
x => new { Name = x.Name, Age = x.Age) // Select the Name and Age columns only
)
// Output: { Name = "Fluffy", Age = 3 }
// Select the entity (no columns specified)
var all = unitOfWork.CatRepository.Get<Cat>(
null, // No filter
null, // No order
string.Empty, // No includeProperties
null) // Select the Name and Age columns only
)
// Output: { Cat() { Name = "Fluffy", Age = 3, Breed = "Moggy" } }
// Same query as above
var allSimple = unitOfWork.CatRepository.Get<Cat>()
// Output: { Cat() { Name = "Fluffy", Age = 3, Breed = "Moggy" } }

Categories