Lots Repositories In Project - c#

I'm using ninject controller factory to bind some repositories to controllers via interfaces...
public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel ninjectKernel;
public NinjectControllerFactory()
{
ninjectKernel = new StandardKernel();
AddBindings();
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) {
return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType);
}
private void AddBindings()
{
ninjectKernel.Bind<ISystemDefinition<CentreModel>>().To<CentreRepository>();
ninjectKernel.Bind<ISystemDefinition<ResidentStatusModel>>().To<ResidentStatusRepository>();
// here will be the list of lots of repositories with different models
}
}
Imagine that i need to write about hundred forms with different data, so i need to have at least different models...(maybe with same "IReposotory"), but i still need to add model into "AddBindings()" method... Is it ok? (I'm not sure)...
What is the best practice to store repositories with different models into controllers?
Model is for example:
public class CentreModel : BaseModel {
[Required(ErrorMessage = "*")]
[Display(Name = "Name:")]
public string Name { get; set; }
[Display(Name = "Order:")]
[Required(ErrorMessage = "*")]
public int SortOrder { get; set; }
[Display(Name = "Is active:")]
public bool IsActive { get; set; }
[Required(ErrorMessage = "*")]
[Display(Name = "Associated centre:")]
public int AssociatedCentreId { get; set; }
public IEnumerable<CentreListModel> OldCentres { get; set; }
}
Interface example:
public interface ISystemDefinition<T> where T : class,new()
{
IEnumerable<T> GetList(bool? isActive = null);
IEnumerable<T> GetList(bool? isActive = null, int? OwnerID = null);
T Fetch(int? ID);
void Save(T item);
void Remove(T item);
void Order(List<T> items);
}
And Repository example:
public class CentreRepository : BaseRepository, ISystemDefinition<CentreModel> {
public IEnumerable<CentreModel> GetList(bool? isActive) {
return (from c in entities.Centres
where c.IsActive == isActive || isActive == null
orderby c.SortOrder
select new CentreModel {
ID = c.ID,
Name = c.Name,
IsActive = c.IsActive,
SortOrder = c.SortOrder,
AssociatedCentreId = c.AssociatedCentreID
});
}
public CentreModel Fetch(int? ID) {
return (from c in entities.Centres
where c.ID == ID
orderby c.SortOrder
select new CentreModel {
ID = c.ID,
Name = c.Name,
IsActive = c.IsActive,
SortOrder = c.SortOrder,
AssociatedCentreId = c.AssociatedCentreID
}).FirstOrDefault();
}
public void Save(CentreModel model) {
Centre centre = model.IsNew ? new Centre() : entities.Centres.Where(c => c.ID == model.ID).SingleOrDefault();
centre.Name = model.Name;
centre.SortOrder = model.SortOrder;
centre.IsActive = model.IsActive;
centre.AssociatedCentreID = model.AssociatedCentreId;
if (model.IsNew) entities.Centres.Add(centre);
Save();
}
public void Remove(CentreModel model) {
Centre centre = model.IsNew ? new Centre() : entities.Centres.Where(c => c.ID == model.ID).SingleOrDefault();
entities.Centres.Remove(centre);
Save();
}
public void Order(List<CentreModel> items) {
throw new NotImplementedException();
}
public IEnumerable<CentreModel> GetList(bool? isActive = null, int? OwnerID = null) {
throw new NotImplementedException();
}
}

Rather than creating a repository for each model you might be better of creating a single concrete generic repository.
EDIT
For instance if you had the following interface:
public interface IRepository<TEntity> : IDisposable where TEntity : class
{
IQueryable<TEntity> GetAll();
IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] includeProperties);
TEntity Find(params object[] keys);
void Add(TEntity entity, bool save = true);
void Edit(TEntity entity, bool save = true);
void Delete(bool save = true, params object[] keys);
void Save();
}
You could have a single concrete implementation(example uses EF):
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
private readonly DbContext _context;
public Repository(DbContext context)
{
if (context == null) throw new ArgumentNullException("context");
_context = context;
}
public IQueryable<TEntity> GetAll()
{
return _context.Set<TEntity>();
}
public IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] includeProperties)
{
IQueryable<TEntity> queryable = GetAll();
return includeProperties.Aggregate(queryable, (current, includeProperty) => current.Include(includeProperty));
}
public TEntity Find(params object[] keys)
{
return _context.Set<TEntity>().Find(keys);
}
... etc.
}
Then you can bind the open generic interface to the open generic repository as below:
Bind(typeof(IRepository<,>)).To(typeof(Repository<,>));
Also for my example you would need to bind the context to be injected into the repository constructor:
Bind<DbContext>().To<MyDbContext>();
This should then correctly instantiate a repository for any kind of model without having to have an explicit binding for each one.
So with the below controller constructor:
protected readonly IRepository<SomeEntity> _repository;
protected BaseController(IRepository<SomeEntity> _repository)
{
_repository = repository;
}
Ninject would inject a Repository<SomeEntity>.

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

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" } }

ASP .NET Core disposing UnitOfWork before getting all data

I'm implementing a generic repository + Unit of work pattern along with a WebApi project. I'm having problems with getting one entity and including the collection of another enttity that refers to it.
I have the following entities mapped through code first:
public class Ingredient : BaseEntity
{
public string Name { get; set; }
public string Amount { get; set; }
public Guid RecipeId { get; set; }
public virtual Recipe Recipe { get; set; }
}
public class Recipe : BaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
public string ImagePath { get; set; }
public virtual ICollection<Ingredient> Ingredients { get; set; }
}
This is my unit of work:
public class UnitOfWork<TContext> : IRepositoryFactory, IUnitOfWork<TContext>, IUnitOfWork where TContext : DbContext
{
private readonly TContext context;
private Dictionary<Type, object> repositories;
public UnitOfWork(TContext context)
{
this.context = context ?? throw new ArgumentNullException(nameof(context));
}
public TContext Context => context;
public IRepository<TEntity> GetRepository<TEntity>() where TEntity : BaseEntity
{
if (repositories == null)
{
repositories = new Dictionary<Type, object>();
}
var type = typeof(TEntity);
if (!repositories.ContainsKey(type))
{
repositories.Add(type, new Repository<TEntity>(context));
}
return (IRepository<TEntity>)repositories[type];
}
public int Commit()
{
return context.SaveChanges();
}
public void Dispose()
{
context?.Dispose();
}
}
And my generic repository:
public class Repository<T> : IRepository<T> where T : BaseEntity
{
protected readonly DbContext dbContext;
protected readonly DbSet<T> dbSet;
public Repository(DbContext context)
{
dbContext = context;
dbSet = dbContext.Set<T>();
}
public T GetEntity(Guid id)
{
return dbSet.Find(id);
}
public T GetEntity(Guid id, params Expression<Func<T, object>>[] includeProperties)
{
IEnumerable<string> properties = GetProperties(includeProperties);
IQueryable<T> queryable = dbSet;
foreach (var property in includeProperties)
{
queryable = dbSet.Include(property);
}
return queryable.FirstOrDefault(x => x.Id == id);
}
[...]
private static IEnumerable<string> GetProperties(Expression<Func<T, object>>[] includeProperties)
{
List<string> includelist = new List<string>();
foreach (var item in includeProperties)
{
MemberExpression body = item.Body as MemberExpression;
if (body == null)
throw new ArgumentException("The body must be a member expression");
includelist.Add(body.Member.Name);
}
return includelist.AsEnumerable();
}
}
The controller is injecting the RecipeService. In the controller I have this method:
[HttpGet("{id}", Name = "Get")]
public IActionResult Get(Guid id)
{
var recipe = recipeService.GetRecipe(id);
if (recipe == null)
{
return NotFound();
}
return Ok(recipe);
}
The recipe service injects the IUnitOfWork and has the following method:
public Recipe GetRecipe(Guid id)
{
return repository.GetEntity(id, r => r.Ingredients);
}
Also I have the services registered as follows:
services.AddScoped<IRepositoryFactory, UnitOfWork<TContext>>();
services.AddScoped<IUnitOfWork, UnitOfWork<TContext>>();
services.AddScoped<IUnitOfWork<TContext>, UnitOfWork<TContext>>();
services.AddScoped<IRecipeService, RecipeService>();
My problem is when I'm getting a specified recipe(along with its ingredients) i got a "The connection has been restarted" error (in firefox). While debugging I can see that I have the recipe with its ingredients. But when returning Ok(recipe) and mapping the result to the entities, the IUnitOfWork is disposed after getting the first ingredient.
Anyone can help me out? Thanks
The problem was I was having a circular reference i wasn't getting any exception.
I fixed by adding the following in the ConfigureServices method of the Startup class:
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});

DbManager helper class for Entity Framework

I'm trying to do a generic Db cotext helper\manager so I can use it as a dll in many projects with different data bases.
I tried to use it with one of my projects but I got confused with the attach and detach in the Add and Edit methods. I'm getting errors as "An entity object cannot be referenced by multiple instances of IEntityChangeTracker."
Could someone help me to get this manager perfect?
public class DbEntityManager<T> : IDisposable where T : class, IEntity
{
public DbContext DbEntities { get; set; }
public virtual int AddNewObject(T objToAdd)
{
DbEntities.Set<T>().Add(objToAdd);
return DbEntities.SaveChanges();
}
public virtual int EditObject(T objToEdit)
{
if (DbEntities.Entry(objToEdit).State == EntityState.Detached)
{
DbEntities.Set<T>().Attach(objToEdit);
DbEntities.Entry(objToEdit).State = EntityState.Modified;
}
else
{
DbEntities.Entry(objToEdit).State = EntityState.Modified;
}
return DbEntities.SaveChanges();
}
public virtual int DeleteObject(T objToDelete)
{
DbEntities.Set<T>().Remove(objToDelete);
return DbEntities.SaveChanges();
}
public virtual List<T> GetAllList()
{
return DbEntities.Set<T>().ToList();
}
public virtual T GetObjectById(int id)
{
return DbEntities.Set<T>().AsEnumerable().SingleOrDefault(x => x.Id == id);
}
public DbEntityManager(DbContext db)
{
DbEntities = db;
}
public void Dispose()
{
this.Dispose();
}
}
EDIT: Trying to explain better what I'm reffering.
The IEntity is an interface just to be able to use the GetObjectById function:
public interface IEntity
{
int Id { get; }
}
Here is an example of a class:
public partial class Address : IEntity
{
public Address()
{
this.Customers = new HashSet<Customer>();
}
public int AddressID { get; set; }
public string Address_Country { get; set; }
public string Address_City { get; set; }
public string Address_Street { get; set; }
public string Address_ZipCode { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
public int Id
{
get { return AddressID; }
}
}
So I can use the code like this: Making a manager just for the class
public class AddressManager : DbEntityManager<Address>
{
public AddressManager()
: base (new MySystemEntities())
{
}
}
And then use it the code (in winform/mvc or whatever):
AddressManager manager = new AddressManager ();
Address address = new Address();
address = manager.GetObjectById(id);
Or:
Address address = new Address();
address.Address_Country = "USA";
manager.AddNewObject(address);
Before I did this I searched the internet to see if a class like this already exist, but I didn't find any. So if anyone knows a class already made with these functions I'd be glad to use it.
I have generally used the Repository along with the UnitOfWork pattern.
Try implementing such a base repository and then created derived entity types using the same.
That will help keep your code DRY.
public interface IBaseRepository<TEntity> : IDisposable
where TEntity : class
{
void Delete(object id);
void Delete(TEntity entity);
TEntity Find(params object[] keyValues);
void Insert(TEntity entity);
void InsertRange(IEnumerable<TEntity> entities);
IQueryable<TEntity> SelectQuery(string query, params object[] parameters);
void Update(TEntity entity);
void RefreshEntityContext(TEntity entity);
}
Hope this helps!
Edit: Ok, here is an extract of the implementation (similar to how I have implemented it):
namespace ContosoUniversity.DAL
{
public class GenericRepository<TEntity> where TEntity : class
{
internal SchoolContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(SchoolContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
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)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TEntity entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
}
You can now inherit your repositories from the GenericRepository and have all the basic CRUD operations available.
from,
http://www.asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
I prefer to do something like the following:
public virtual int EditObject(T objToEdit)
{
var curval = DB.Set<T>().FirstOrDefault(x => x.Id == objToEdit.Id);
//You may want to make all your entities inherit from the same
//baseclass to get Id
if (curval == null)
{
DB.Set<T>.Add(objToEdit);
}
else
{
DB.Entry(curval).CurrentValues.SetValues(objToEdit);
DB.Entry(curval).State = System.Data.Entity.EntityState.Modified;
}
return DB.SaveChanges();
}
it's quite easy to modify this code to work with Models too.
Why reinvent the wheel. Try NRepository, it's a generic repository (yes, another one) with an entity framework extension and uses strategies for reuse rather then service methods.Plus its fully testable without mocks.
IQueryRepository queryRepository = new EntityFrameworkQueryRepository(new YourDbContext()) ;
var address = queryRepository.GetEntity<Address>(
p => p.AddressID == 2,
new AsNoTrackingQueryStrategy(),
new EagerLoadingQueryStrategy<Address>(
p => p.Customers));
Or to add entities as well as just use :
IRepository repository = new EntityFrameworkRepository(new YourDbContext()) ;
var address = new Address{ Address_City = "Leeds" };
repository.Add(address);
repository.Save()
N.B. Don't forget to inject in your repositories :)
entity framework already use repository and unit of work design pattern, no need for an helpers to work with that framework

Multi-tenancy web application with filtered dbContext

I am new to ASP.Net MVC and multi-tenancy web application. I have done lots of reading, but being a beginner I just follow what I understand. So I managed to built a sample scenario web application and need to solve the ending part of it. Hope this scenario will be useful for some other beginners as well, but would welcome any other approach. Thanks in advance
1) Database in SQLServer 2008.
2) Data layer: C# class library project called MyApplication.Data
public class AppUser
{
[Key]
public virtual int AppUserID { get; set; }
[Required]
public virtual int TenantID { get; set; }
[Required]
public virtual int EmployeeID { get; set; }
[Required]
public virtual string Login { get; set; }
[Required]
public virtual string Password { get; set; }
}
public class Employee
{
[Key]
public virtual int EmployeeID { get; set; }
[Required]
public virtual int TenantID { get; set; }
[Required]
public virtual string FullName { get; set; }
}
public class Tenant_SYS
{
//this is an autonumber starting from 1
[Key]
public virtual int TenantID { get; set; }
[Required]
public virtual string TenantName { get; set; }
}
3). Business Layer: class library MyApplication.Business
Following FilteredDbSet Class courtesy: Zoran Maksimovic
public class FilteredDbSet<TEntity> : IDbSet<TEntity>, IOrderedQueryable<TEntity>, IOrderedQueryable, IQueryable<TEntity>, IQueryable, IEnumerable<TEntity>, IEnumerable, IListSource
where TEntity : class
{
private readonly DbSet<TEntity> _set;
private readonly Action<TEntity> _initializeEntity;
private readonly Expression<Func<TEntity, bool>> _filter;
public FilteredDbSet(DbContext context)
: this(context.Set<TEntity>(), i => true, null)
{
}
public FilteredDbSet(DbContext context, Expression<Func<TEntity, bool>> filter)
: this(context.Set<TEntity>(), filter, null)
{
}
public FilteredDbSet(DbContext context, Expression<Func<TEntity, bool>> filter, Action<TEntity> initializeEntity)
: this(context.Set<TEntity>(), filter, initializeEntity)
{
}
public Expression<Func<TEntity, bool>> Filter
{
get { return _filter; }
}
public IQueryable<TEntity> Include(string path)
{
return _set.Include(path).Where(_filter).AsQueryable();
}
private FilteredDbSet(DbSet<TEntity> set, Expression<Func<TEntity, bool>> filter, Action<TEntity> initializeEntity)
{
_set = set;
_filter = filter;
MatchesFilter = filter.Compile();
_initializeEntity = initializeEntity;
}
public Func<TEntity, bool> MatchesFilter
{
get;
private set;
}
public IQueryable<TEntity> Unfiltered()
{
return _set;
}
public void ThrowIfEntityDoesNotMatchFilter(TEntity entity)
{
if (!MatchesFilter(entity))
throw new ArgumentOutOfRangeException();
}
public TEntity Add(TEntity entity)
{
DoInitializeEntity(entity);
ThrowIfEntityDoesNotMatchFilter(entity);
return _set.Add(entity);
}
public TEntity Attach(TEntity entity)
{
ThrowIfEntityDoesNotMatchFilter(entity);
return _set.Attach(entity);
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, TEntity
{
var entity = _set.Create<TDerivedEntity>();
DoInitializeEntity(entity);
return (TDerivedEntity)entity;
}
public TEntity Create()
{
var entity = _set.Create();
DoInitializeEntity(entity);
return entity;
}
public TEntity Find(params object[] keyValues)
{
var entity = _set.Find(keyValues);
if (entity == null)
return null;
// If the user queried an item outside the filter, then we throw an error.
// If IDbSet had a Detach method we would use it...sadly, we have to be ok with the item being in the Set.
ThrowIfEntityDoesNotMatchFilter(entity);
return entity;
}
public TEntity Remove(TEntity entity)
{
ThrowIfEntityDoesNotMatchFilter(entity);
return _set.Remove(entity);
}
/// <summary>
/// Returns the items in the local cache
/// </summary>
/// <remarks>
/// It is possible to add/remove entities via this property that do NOT match the filter.
/// Use the <see cref="ThrowIfEntityDoesNotMatchFilter"/> method before adding/removing an item from this collection.
/// </remarks>
public ObservableCollection<TEntity> Local
{
get { return _set.Local; }
}
IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
{
return _set.Where(_filter).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _set.Where(_filter).GetEnumerator();
}
Type IQueryable.ElementType
{
get { return typeof(TEntity); }
}
Expression IQueryable.Expression
{
get
{
return _set.Where(_filter).Expression;
}
}
IQueryProvider IQueryable.Provider
{
get
{
return _set.AsQueryable().Provider;
}
}
bool IListSource.ContainsListCollection
{
get { return false; }
}
IList IListSource.GetList()
{
throw new InvalidOperationException();
}
void DoInitializeEntity(TEntity entity)
{
if (_initializeEntity != null)
_initializeEntity(entity);
}
public DbSqlQuery<TEntity> SqlQuery(string sql, params object[] parameters)
{
return _set.SqlQuery(sql, parameters);
}
}
public class EFDbContext : DbContext
{
public IDbSet<AppUser> AppUser { get; set; }
public IDbSet<Tenant_SYS> Tenant { get; set; }
public IDbSet<Employee> Employee { get; set; }
///this makes sure the naming convention does not have to be plural
///tables can be anything we name them to be
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public EFDbContext(int tenantID = 0) //Constructor of the class always expect a tenantID
{
//Here, the Dbset can expose the unfiltered data
AppUser = new FilteredDbSet<AppUser>(this);
Tenant = new FilteredDbSet<Tenant_SYS>(this);
//From here, add all the multitenant dbsets with filtered data
Employee = new FilteredDbSet<Employee>(this, d => d.TenantID == tenantID);
}
}
public interface IEmployeeRepository
{
IQueryable<Employee> Employees { get; }
void SaveEmployee(Employee Employee);
void DeleteEmployee(Employee Employee);
List<Employee> GetEmployeesSorted();
}
public class EFEmployeeRepository : IEmployeeRepository
{
private EFDbContext context;
public EFEmployeeRepository(int tenantID = 0)
{
context = new EFDbContext(tenantID);
}
IQueryable<Employee> IEmployeeRepository.Employees
{
get
{
return context.Employee;
}
}
public void SaveEmployee(Employee Employee)
{
if (Employee.EmployeeID == 0)
{
context.Employee.Add(Employee);
}
context.SaveChanges();
}
public void DeleteEmployee(Employee Employee)
{
context.Employee.Remove(Employee);
context.SaveChanges();
}
public List<Employee> GetEmployeesSorted()
{
//This is just a function to see the how the results are fetched.
return context.Employee.OrderBy(m => m.FullName)
.ToList();
//I haven't used where condition to filter the employees since it should be handled by the filtered context
}
}
4) WEB Layer: ASP.NET MVC 4 Internet Application with Ninject DI
public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel ninjectKernel;
public NinjectControllerFactory()
{
ninjectKernel = new StandardKernel();
AddBindings();
}
protected override IController GetControllerInstance(RequestContext requestContext,
Type controllerType)
{
return controllerType == null
? null
: (IController)ninjectKernel.Get(controllerType);
}
private void AddBindings()
{
ninjectKernel.Bind<IAppUserRepository>().To<EFAppUserRepository>();
ninjectKernel.Bind<IEmployeeRepository>().To<EFEmployeeRepository>();
}
}
5) Controller. Here is the Problem
public class HomeController : Controller
{
IEmployeeRepository repoEmployee;
public HomeController(IEmployeeRepository empRepository)
{
//How can I make sure that the employee is filtered globally by supplying a session variable of tenantID
//Please assume that session variable has been initialized from Login modules after authentication.
//There will be lots of Controllers like this in the application which need to use these globally filtered object
repoEmployee = empRepository;
}
public ActionResult Index()
{
//The list of employees fetched must belong to the tenantID supplied by session variable
//Why this is needed is to secure one tenant's data being exposed to another tenants accidently like, if programmer fails to put where condition
List<Employee> Employees = repoEmployee.Employees.ToList();
return View();
}
}
NInject DI can do the magic !! Provided you will have a login routine which creates the session variable "thisTenantID".
In the Web Layer:
private void AddBindings()
{
//Modified to inject session variable
ninjectKernel.Bind<EFDbContext>().ToMethod(c => new EFDbContext((int)HttpContext.Current.Session["thisTenantID"]));
ninjectKernel.Bind<IAppUserRepository>().To<EFAppUserRepository>();
ninjectKernel.Bind<IEmployeeRepository>().To<EFEmployeeRepository>().WithConstructorArgument("tenantID", c => (int)HttpContext.Current.Session["thisTenantID"]);
}
The way you have designed your repository follows a very clear design, but the parameter that you are passing in the constructor makes things a bit more complicated when using dependency injection.
What I propose here below, is perhaps not the best design, but it will allow you to progress without doing too much changes to your existing code.
The catch in this solution is that you have to call the "Initialise" method when creating the controller, which potentially you might not like, but it is quite effective.
Here are the steps:
Create a new method in your IEmployeeRepository
public interface IEmployeeRepository
{
//leave everything else as it is
void Initialise(int tenantId);
}
Implement that method in the EFEmployeeRepository
public class EFEmployeeRepository
{
//leave everything else as it is
public void Initialise(int tenantID = 0)
{
context = new EFDbContext(tenantID);
}
}
In the HomeController, you would need to call "Initialise" in the constructor
public HomeController(IEmployeeRepository empRepository)
{
repoEmployee = empRepository;
repoEmployee.Initialise(/* use your method to pass the Tenant ID here*/);
}
An alternative to this approach could be to create a RepositoryFactory that would return the Repository filled out with all the filters you need. In that case you will inject the Factory rather than the Repository to the Controller.

Categories