I'm thinking of a way to apply the pattern ( multi-tenant ), raised on this issue ( Standard that has contributed to the reliability of software that needs to meet complex models such as multi-business models ) EntityFramework .
As the title of the issue presents, the standard is based on a way to protect the application so that data from a Reseller, its client companies and its customers do not have their data presented to others.
I believe the multi-tenant (" multi-tenant ") would be appropriate for this case - even if it is a single operator of a single company accessing the various tenants. This article briefly describes the philosophy behind the multitenancy .
It's a multi-profile access application.
So, I thought about doing this in a base class for the repositories. So I started by creating something like this:
An interface to set a pattern:
public interface IRepository<TContext, T, TKey> : IDisposable
where TContext : DbContext, new()
where T : class
{
T Get(TKey id);
IQueryable<T> GetAll(Expression<Func<T, bool>> filter);
IQueryable<T> Query(params Expression<Func<T, object>>[] includes);
T Add(T entity);
List<T> AddRange(List<T> items);
bool Edit(T entity);
bool Delete(TKey id);
bool Delete(T entity);
int Delete(Expression<Func<T, bool>> where);
int SaveChanges();
}
And an abstract class, which implements this interface, to be inherited by the repository classes:
public abstract class CustomRepository<TContext, T, TKey> : IRepository<TContext, T, TKey>
where TContext : DbContext, new()
where T : class
{
private TContext _context = null;
private bool _responsibleContext = false;
/// <summary>
/// constructor
/// </summary>
public CustomRepository()
{
_context = new TContext();
_responsibleContext = true;
}
/// <summary>
/// constructor with a DbContext param
/// </summary>
/// <param name="context">A DbContext param</param>
public CustomRepository(TContext context)
{
_context = context;
_responsibleContext = false;
}
/// <summary>
/// disposer
/// </summary>
public void Dispose()
{
if (_responsibleContext && _context != null)
_context.Dispose();
GC.SuppressFinalize(this);
}
#region other interface implementations ...
public T Get(TKey id)
{
return _context.Set<T>().Find(id);
}
public IQueryable<T> GetAll(Expression<Func<T, bool>> filter)
{
return Query().Where(filter);
}
public IQueryable<T> Query(params Expression<Func<T, object>>[] includes)
{
IQueryable<T> set = _context.Set<T>();
foreach (var include in includes)
set = set.Include(include);
return set;
}
public T Add(T entity)
{
_context.Set<T>().Add(entity);
SaveChanges();
return entity;
}
public List<T> AddRange(List<T> items)
{
_context.Set<T>().AddRange(items);
SaveChanges();
return items;
}
public bool Edit(T entity)
{
var result = false;
_context.Entry<T>(entity).State = EntityState.Modified;
if (SaveChanges() > 0)
result = true;
return result;
}
public bool Delete(TKey id)
{
var entity = Get(id);
return Delete(entity);
}
public bool Delete(T entity)
{
_context.Entry(entity).State = EntityState.Deleted;
return SaveChanges() > 0;
}
public int Delete(Expression<Func<T, bool>> where)
{
var entries = Query().Where(where);
_context.Set<T>().RemoveRange(entries);
return SaveChanges();
}
public int SaveChanges()
{
return _context.SaveChanges();
}
#endregion other interface implementations ...
}
And if I understood the standard proposal well, I would have to pass an instance reference from my User's session, so I could change the methods to something like:
public T Get(TKey id)
{
var entry = _context.Set<T>().Find(id);
if (entry.RevendaId == userSession.RevendaId &&
entry.EmpresaId == userSession.EmpresaId &&
entry.ClienteId == userSession.ClienteId)
return entry;
else
return null;
}
And also:
public IQueryable<T> Query(params Expression<Func<T, object>>[] includes)
{
IQueryable<T> set = _context.Set<T>();
foreach (var include in includes)
set = set.Include(include);
set = set.Where(x => x.RevendaId == userSession.RevendaId &&
x.EmpresaId == userSession.EmpresaId &&
x.ClienteId == userSession.ClienteId);
return set;
}
And apply this approach in all methods.
Well, I have two questions :
Would this approach attempt to implement the pattern be a correct one?
1.1. If not, what would a correct implementation of this pattern be in the scenario presented?
If yes, how would I pass a Session User instance to the repository instance?