Repository Pattern without Edit - c#

I am reading about the Repository Pattern in C#. Every article I read with a sample repository looks like below and does not contain a definition for Edit entity.
TEntity Get(TKey id);
void Save(TEntity entity);
void Delete(TEntity entity);
Is it bad idea to have Edit method in base repository interface like below?
public interface IRepositor<T>
{
void Edit(Predicate<T> predicate, Action<T> action);
}
public class Car : IRepository<Car>
{
public void Edit(Predicate<Car> predicate, Action<Car> action)
{
var car = Context.Cars.SingleOrDefault(x => predicate(x));
if(car == null) throw new Exception("Not found");
action.Invoke(car);
}
}
public class CarController
{
public void Edit(CarViewModel vm)
{
carService.Edit(x => x.Id == vm.Id, y => y.Type == "BMW" );
}
}

Related

Delete all corresponding items from SQLite

I have the following statement which get all the list item and then do loop to delete each item from the database.
Is there a better way to handle deleting all items?
IRepository.cs
public interface IRepository<T> where T : new()
{
TableQuery<T> AsQueryable();
T GetById(object id);
List<T> GetWithChildren(Expression<Func<T, bool>> predicate = null);
void Delete(T entity);
void CreateTable();
void Clear();
}
AnotherService.cs
protected IRepository<TModel> Repository => _repository ?? (_repository = new Repository<TModel>(_factory));
Service.cs
private IRepository<Class> _classRepo;
public void DeleteClasses(string classId)
{
foreach (var class in _classRepo.Get(x => x.ClassId.Equals(classId)).ToList())
{
_classRepo.Delete(class);
}
}

Why am I getting the error "Cannot instantiate implementation type" for my generic service?

I have a generic repository that I have been instantiating right in my WEB API Controller no problem for a while.
This is what my controller used to look like:
[Route("api/[controller]")]
public class EmployeesController : Controller
{
private IGenericRepository<Employee> _empRepo;
public EmployeesController(IGenericRepository<Employee> employeeRepo)
{
_empRepo = employeeRepo;
}
// GET: api/employees
[HttpGet]
public async Task<IEnumerable<Employee>> GetEmployeesAsync(
string firstName = null, string lastName = null)
{
//return await _empRepo.GetAll().Include("Organization").Include("PayPlan").Include("GradeRank").Include("PositionTitle").Include("Series").Include("BargainingUnit")
// .Where(e => (string.IsNullOrEmpty(firstName) || e.FirstName.Contains(firstName))
// && (string.IsNullOrEmpty(lastName) || e.LastName.Contains(lastName))
// )
// .ToListAsync();
return await _empRepo.GetAllIncluding(
a => a.Organization,
b => b.PayPlan,
c => c.GradeRank,
d => d.PositionTitle,
e => e.Series,
f => f.BargainingUnit)
.Where(e => (string.IsNullOrEmpty(firstName) || e.FirstName.Contains(firstName))
&& (string.IsNullOrEmpty(lastName) || e.LastName.Contains(lastName))
)
.ToListAsync();
}
// GET api/employees/5
[HttpGet("{id}", Name = "GetEmployeeById")]
public async Task<IActionResult> GetEmployeeByIdAsync(long id)
{
//var employee = await _empRepo.Find(id).Include("Organization").Include("PayPlan").Include("GradeRank").Include("PositionTitle").Include("Series").Include("BargainingUnit").SingleAsync();
var employee = await _empRepo.GetSingleIncludingAsync(id,
a => a.Organization,
b => b.PayPlan,
c => c.GradeRank,
d => d.PositionTitle,
e => e.Series,
f => f.BargainingUnit);
if (employee == null)
{
return NotFound();
}
else
{
return new ObjectResult(employee);
}
}
// PUT api/employees/id
[HttpPut("{id}")]
public async Task<IActionResult> PutEmployeeAsync([FromBody] Employee emp)
{
var employee = await _empRepo.UpdateAsync(emp);
if (employee == null)
{
return NotFound();
}
await _empRepo.SaveAsync();
return new ObjectResult(employee);
}
}
and we would configure the DI in StartUp like this:
services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
Now I have just refactored and tried to put a service layer in between the controller and the generic repo.
So here is my second DI line in StartUp:
services.AddScoped(typeof(IGenericService<>), typeof(IGenericService<>));
So now I have these two DI lines:
services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddScoped(typeof(IGenericService<>), typeof(IGenericService<>));
The following is my current code:
Starting with the Generic Repo:
public enum FilteredSource
{
All,
GetAllIncluding,
}
public class GenericRepository<T> : IGenericRepository<T>
where T: BaseEntity
{
protected readonly ApplicationDbContext _context;
protected DbSet<T> _dbSet;
public GenericRepository(ApplicationDbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
// no eager loading
private IQueryable<T> All => _dbSet.Cast<T>();
#region FIXME : DELETE
// FIXME: Delete and use ALL instead.
public IQueryable<T> GetAll() => _dbSet.AsQueryable();
// FIXME: Delete and use GetSingleIncludingAsync instead.
public IQueryable<T> Find(long id) =>
_dbSet.Where(e => e.Id == id).AsQueryable();
#endregion
// eager loading
private IQueryable<T> GetAllIncluding(
params Expression<Func<T, object>>[] includeProperties) =>
includeProperties.Aggregate(All, (currentEntity, includeProperty) => currentEntity.Include(includeProperty));
// no eager loading
public async Task<T> GetSingleIncludingAsync(long id)
{
return await _dbSet.SingleOrDefaultAsync(e => e.Id == id);
}
/// <summary>
/// Takes in a lambda selector and let's you filter results from GetAllIncluding and All.
/// </summary>
/// <param name="selector">labmda expression to filter results by.</param>
/// <param name="getFilteredSource">All or GetAllIncluding as the method to get results from.</param>
/// <param name="includeProperties">array of eager load lamda expressions.</param>
/// <returns></returns>
public async Task<IEnumerable<T>> GetFiltered(
Expression<Func<T, bool>> selector, FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
var results = default(IEnumerable<T>);
switch (filteredSource)
{
case FilteredSource.All:
results = All.Where(selector);
break;
case FilteredSource.GetAllIncluding:
results = GetAllIncluding(includeProperties).Where(selector);
break;
}
return await results.AsQueryable().ToListAsync();
}
// eager loading
public async Task<T> GetSingleIncludingAsync(
long id, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> entities = GetAllIncluding(includeProperties);
//return await Filter<long>(entities, x => x.Id, id).FirstOrDefaultAsync();
return await entities.SingleOrDefaultAsync(e => e.Id == id);
}
public async Task<T> InsertAsync(T entity)
{
if (entity == null)
{
throw new ArgumentNullException($"No {nameof(T)} Entity was provided for Insert");
}
await _dbSet.AddAsync(entity);
return entity;
}
public async Task<T> UpdateAsync(T entity)
{
T entityToUpdate = await
_dbSet.AsNoTracking().SingleOrDefaultAsync(e => e.Id == entity.Id);
if (entityToUpdate == null)
{
//return null;
throw new ArgumentNullException($"No {nameof(T)} Entity was provided for Update");
}
_dbSet.Update(entity);
return entity;
}
public async Task<T> DeleteAsync(T entity)
{
_dbSet.Remove(entity);
return await Task.FromResult(entity);
}
public Task SaveAsync() => _context.SaveChangesAsync();
Interface Definition:
public interface IGenericRepository<T>
where T : BaseEntity
{
#region FIXME : DELETE
// FIXME: Delete and use ALL instead.
IQueryable<T> GetAll();
// FIXME: Delete and use GetSingleIncludingAsync instead.
IQueryable<T> Find(long id);
#endregion
// eager loading
Task<T> GetSingleIncludingAsync(
long id, params Expression<Func<T, object>>[] includeProperties);
Task<IEnumerable<T>> GetFiltered(
Expression<Func<T, bool>> selector, FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null);
Task<T> InsertAsync(T entity);
Task<T> UpdateAsync(T entity);
Task<T> DeleteAsync(T entity);
#region Possible TODOs:
//Task<IEnumerable<T>> FindBy(Expression<Func<T, bool>> predicate);
//Task AddRange(IEnumerable<T> entities);
//Task RemoveRange(IEnumerable<T> entities);
#endregion
Task SaveAsync();
}
This gets injected into my Generic Service:
public class GenericService<T> : IGenericService<T>
where T : BaseEntity
{
private IGenericRepository<T> _genericRepo;
public GenericService(IGenericRepository<T> genericRepo)
{
_genericRepo = genericRepo;
}
public async Task<IEnumerable<T>> GetFiltered(
Expression<Func<T, bool>> selector, FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
return await _genericRepo.GetFiltered(selector, filteredSource,
includeProperties);
}
// eager loading
public async Task<T> GetSingleIncludingAsync(long id, params Expression<Func<T, object>>[] includeProperties)
{
IEnumerable<T> entities = await _genericRepo.GetFiltered(null, FilteredSource.GetAllIncluding, includeProperties);
//return await Filter<long>(entities, x => x.Id, id).FirstOrDefaultAsync();
return entities.SingleOrDefault(e => e.Id == id);
}
public async Task<T> InsertAsync(T entity)
{
var result = await _genericRepo.InsertAsync(entity);
await _genericRepo.SaveAsync();
return entity;
}
public async Task<T> UpdateAsync(T entity)
{
var result = await _genericRepo.UpdateAsync(entity);
if (result != null)
{
await _genericRepo.SaveAsync();
}
return result;
}
public async Task<T> DeleteAsync(T entity)
{
throw new NotImplementedException();
}
}
Interface Definition for the Service:
public interface IGenericService<T>
where T : BaseEntity
{
Task<IEnumerable<T>> GetFiltered(
Expression<Func<T, bool>> selector, FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null);
// eager loading
Task<T> GetSingleIncludingAsync(long id, params Expression<Func<T, object>>[] includeProperties);
Task<T> InsertAsync(T entity);
Task<T> UpdateAsync(T entity);
Task<T> DeleteAsync(T entity);
}
and finally, here is the controller:
[Route("api/[controller]")]
public class EmployeesController : Controller
{
private IGenericService<Employee> _genericService;
public EmployeesController(IGenericService<Employee> genericService)
{
_genericService = genericService;
}
// GET: api/employees
[HttpGet]
public async Task<IEnumerable<Employee>> GetEmployeesAsync(
string firstName = null, string lastName = null)
{
return await _genericService.GetFiltered(
e => (string.IsNullOrEmpty(firstName) || e.FirstName.Contains(firstName))
&& (string.IsNullOrEmpty(lastName) || e.LastName.Contains(lastName)),
FilteredSource.GetAllIncluding,
new Expression<Func<Employee, object>>[] { a => a.Organization,
b => b.PayPlan,
c => c.GradeRank,
d => d.PositionTitle,
e => e.Series,
f => f.BargainingUnit }
);
}
// GET api/employees/5
[HttpGet("{id}", Name = "GetEmployeeById")]
public async Task<IActionResult> GetEmployeeByIdAsync(long id)
{
//var employee = await _empRepo.Find(id).Include("Organization").Include("PayPlan").Include("GradeRank").Include("PositionTitle").Include("Series").Include("BargainingUnit").SingleAsync();
var employee = await _genericService.GetSingleIncludingAsync(id,
a => a.Organization,
b => b.PayPlan,
c => c.GradeRank,
d => d.PositionTitle,
e => e.Series,
f => f.BargainingUnit);
if (employee == null)
{
return NotFound();
}
else
{
return new ObjectResult(employee);
}
}
// PUT api/employees/id
[HttpPut("{id}")]
public async Task<IActionResult> PutEmployeeAsync([FromBody] Employee emp)
{
var employee = await _genericService.UpdateAsync(emp);
if (employee == null)
{
return NotFound();
}
return new ObjectResult(employee);
}
}
I'm just not sure how to plug in the middle service layer.
This happened to me recently. I had the implementation class marked "abstract", an artifact of an earlier design. I removed it and the container was able to instantiate the class without a problem.
I'm an idiot.
I had the Inteface for the implementation.
I changed:
services.AddScoped(typeof(IGenericService<>), typeof(IGenericService<>));
to
services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));
Great question and great answers so far, however...
If your problem is different but the error message is the same
...and you are registering the service for a default implementation, without specifying the implementation type, like here:
services.AddScoped<IMyDependency>();
Then the error is unhelpful because it does not give details on why things don't work:
Cannot instantiate implementation type 'MyApp.IMyDependency' for service type 'MyApp.IMyDependency'.
In that case, change the service registration to:
services.AddScoped<IMyDependency, MyDependency>();
That will improve the error message and it will tell you where is the problem, like so:
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: MyApp.IMyDependency Lifetime: Transient ImplementationType: MyApp.MyDependency': Unable to resolve service for type 'MyApp.ILowerDependency' while attempting to activate 'MyApp.IMyDependency'.)
In the default ASP.NET core DI container, entire dependency graph needs to be registered explicitly (except maybe for some exceptions like ILogger)
Following on Bartosz answer, another reason for this message is if you are trying to satisfy a non-generic interface with a generic concrete type like this:
services.AddScoped(typeof(IMyDependency), typeof(MyDependency<,>));
This won't work because the DI container doesn't know what generic arguments to provide when instantiating MyDependency. Either the interface has to be converted to generic or you need a factory to instantiate it manually.
This happened to me when service class didn't inherit from IService
interface
Based on David's answer, I had to remove the 'abstract' in my service implementation class. However I had some abstract methods and properties in the class too. So after making them non-abstract and creating a public constructor I was able to register the service.

Moq - setup Where extension?

How to set up where linq extension on object? DbSet in my case. Here is my code:
this.workflowStateSet
.Setup(m => m.Where(It.IsAny<Expression<Func<Model.WorkflowState, int, bool>>>()))
.Returns(new List<Model.WorkflowState>().AsQueryable());
However, it gives me exception not very familiar exception:
System.NotSupportedException: Expression references a method that
does not belong to the mocked object: m => m.Where<WorkflowState>
I will be grateful for any hint.
This extension method will help mock the DbSet
public static class MockDbSetExtensions {
public static Mock<DbSet<T>> AsDbSetMock<T>(this IEnumerable<T> list) where T : class {
IQueryable<T> queryableList = list.AsQueryable();
Mock<DbSet<T>> dbSetMock = new Mock<DbSet<T>>();
dbSetMock.As<IQueryable<T>>().Setup(x => x.Provider).Returns(queryableList.Provider);
dbSetMock.As<IQueryable<T>>().Setup(x => x.Expression).Returns(queryableList.Expression);
dbSetMock.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(queryableList.ElementType);
dbSetMock.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(() => queryableList.GetEnumerator());
return dbSetMock;
}
}
And you can use it like this.
//Arrange
var data = new List<Model.WorkflowState>();
//you would populate your list as needed.
//convert it to a mock DbSet that uses the list as its datasource
var workflowStateSet = data.AsDbSetMock();
var dbSet = workflowStateSet.Object;
//Act
var items = dbSet.Where("Your expression here");
//Assert
//....
Use the repository pattern to add a layer of abstraction to the data retrieval. This abstraction can then be mocked.
If, for example, you were trying to retrieve all of the workflows with a stateId equal to 1, then rather than calling something like this
var result = DbSet.WorkflowState.Where(w => w.stateId == 1);
move this code into another class and then create an interface of the method signature.
public interface IWorkflowStateSetRepository{
IQueryable<Model.WorkflowState> GetAllWorkflows(int state);
}
implementation
public class WorkflowStateSetRepository : IWorkflowStateSetRepository{
public IQueryable<Model.WorkflowState> GetAllWorkflows(int state){
return DbSet.WorkflowState .Where(w => w.stateId == state);
}
}
In the calling code get an instance of IWorkflowStateSetRepository (probably from your IoC container) and call the GetAllWorkflows() method instead. This will give you the same results as before but you can now mock the interface in your tests and setup calls to that methods.
this.MockedIWorkflowStateSetRepository.Setup(m => m.GetAllWorkflows(It.IsAny<int>()))
.Returns(new List<Model.WorkflowState>().AsQueryable());
This code is more maintainable and (with appropriately named variables and methods) also conveys the intent a lot better.
The repository pattern is discussed in greater detail here;
http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Are you trying to Mock up a real DbSet instance ? Cause this won't work, as the error message try to explain you. To mock a type, it must either be an interface or have virtual members (abstract members are also virtual).
You can try to mock up IDbSet or to create a custom DbSet class, for instance something like the following class
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
public class DbSetMock<T> : DbSet<T>, IDbSet<T>
where T : class
{
private readonly ICollection<T> _contentCollection;
public DbSetMock(IList<T> contentCollection = null)
{
_contentCollection = new Collection<T>(contentCollection ?? new List<T>());
AddedEntities = new List<T>();
RemovedEntities = new List<T>();
AttachedEntities = new List<T>();
}
public void OverrideContentCollection(IEnumerable<T> newData)
{
_contentCollection.Clear();
_contentCollection.AddRange(newData);
}
public IList<T> AddedEntities { get; private set; }
public IList<T> AttachedEntities { get; private set; }
public override ObservableCollection<T> Local
{
get
{
throw new NotImplementedException();
}
}
public IList<T> RemovedEntities { get; private set; }
public Type ElementType
{
get
{
return typeof(T);
}
}
public Expression Expression
{
get
{
return _contentCollection.AsQueryable().Expression;
}
}
public IQueryProvider Provider
{
get
{
return _contentCollection.AsQueryable().Provider;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return _contentCollection.GetEnumerator();
}
public override T Add(T entity)
{
AddedEntities.Add(entity);
_contentCollection.Add(entity);
return entity;
}
public override T Attach(T entity)
{
AttachedEntities.Add(entity);
var matchingEntity = _contentCollection.SingleOrDefault(x => x.Id == entity.Id);
if (matchingEntity != null)
{
_contentCollection.Remove(matchingEntity);
}
_contentCollection.Add(entity);
return entity;
}
public override TDerivedEntity Create<TDerivedEntity>()
{
throw new NotImplementedException();
}
public override T Create()
{
throw new NotImplementedException();
}
public override T Find(params object[] keyValues)
{
throw new NotImplementedException();
}
public override T Remove(T entity)
{
RemovedEntities.Add(entity);
_contentCollection.Remove(entity);
return entity;
}
}
You can use constructor parameter to setup content that will be retrieved by the db set.
Hope this helps.

Generic permission manager pattern

What I'm trying to do is to create a class with static methods to manage rights of different user types on some types of resources (which are NHibernate entity objects). Specifically I'd like to check the current principal (in an asp.net MVC project) against an object id, to see if he can view or edit an entity. The signature I have in mind is the following:
PermissionManager.CanView<TEntity>(object id);
By now I've done these steps:
1) an interface like this:
public interface ICanAccessQuery<TAccount, TEntity>
where TAccount : IAccountOwner
{
bool CanView(TAccount user, object entityKey);
bool CanEdit(TAccount user, object entityKey);
}
2) some implementations like this one:
public class TeacherCanAccessCourseReportsQuery : ICanAccessQuery<Teacher, CourseReport>
{
public bool CanView(Teacher user, object entityKey)
{
var predicate = PredicateBuilder.Create<CourseReport>(x => x.Id == (long)entityKey);
var conditions = PredicateBuilder.Create<CourseReport>(x => x.Teacher.Id == user.Id);
conditions = conditions.Or(x => x.Teacher.Tutor.Id == user.Id);
conditions = conditions.Or(x => x.CoachingTeachers.Any(t => t.Id == user.Id));
predicate = predicate.And(conditions);
return RepositoryProvider.Get<CourseReport>().Count(predicate) > 0;
}
public bool CanEdit(Teacher user, object entityKey)
{
// similar implementation
}
}
3) a static Configure() method inside my PermissionManager class, to be called in Global.asax:
public static IDictionary<string, object> _permissions = new Dictionary<string, object>();
public static void Configure()
{
_permissions.Add(typeof(Teacher).Name + typeof(CourseReport).Name, new TeacherCanAccessCourseReportsQuery());
}
4) inside the PermissionManager class:
public static bool CanView<TEntity>(object primaryKey, params string[] enabledRoles)
{
var accounts = RepositoryProvider.Get<Account, AccountRepository>();
var principal = Thread.CurrentPrincipal as MyCustomPrincipal;
if (enabledRoles.Any(r => principal.IsInRole(r)))
return true;
IAccountOwner user = accounts.GetUser(principal.AccountId);
var can = false;
var #switch = new Dictionary<Type, Action> {
{ typeof(Teacher), () => can = CanView<Teacher, TEntity>(user as Teacher, primaryKey) },
{ typeof(TrainingCenter), () => can = CanView<TrainingCenter, TEntity>(user as TrainingCenter, primaryKey) }
};
#switch[user.GetType()]();
return can;
}
private static bool CanView<TAccount, TEntity>(TAccount user, object primaryKey)
where TAccount : IAccountOwner
{
var key = typeof(TAccount).Name + typeof(TEntity).Name;
if (_permissions.ContainsKey(key))
{
return (((ICanAccessQuery<TAccount, TEntity>)_permissions[key]).CanView(user, primaryKey);
}
return false;
}
The same methods would be defined for CanEdit... perfectly identical except the method name to be called.
What I'm asking is: is there a better way to define what I have in mind, in a more OOP-way?
I've implemented a better solution that maybe can be interesting for someone.
This is the interface of a "Can I access?" query:
public interface ICanAccessQuery<TAccount, TEntity>
where TAccount : IAccountOwner
where TEntity : IStoredEntity
{
bool CanView(TAccount user, TEntity entity);
bool CanEdit(TAccount user, TEntity entity);
}
My entities now implement an empty interface IStoredEntity to make a constraint.
And this is an example of implementation:
public class TeacherCanAccessOrdersQuery : ICanAccessQuery<Teacher, Order>
{
public bool CanView(Teacher user, Order entity)
{
var predicate = PredicateBuilder.Create<Order>(x => x.Id == entity.Id && x => x.Account.Id == user.Account.Id);
return RepositoryProvider.Get<Order>().Count(predicate) > 0;
}
public bool CanEdit(Teacher user, Order entity)
{
// similar implementation
}
}
Finally, my new AuthorizationProvider class (changed name from PermissionManager, didn't like it):
public class AuthorizationProvider
{
public enum Abilities
{
View,
Edit
};
private static IDictionary<string, object> _authorizations = new Dictionary<string, object>();
// this method should be called at application bootstrap, such as Global.asax in an asp.net app
public static void Configure()
{
_authorizations.Add(typeof(Teacher).Name + typeof(CourseReport).Name, new TeacherCanAccessCourseReportsQuery());
_authorizations.Add(typeof(Teacher).Name + typeof(Order).Name, new TeacherCanAccessOrdersQuery());
// other rules user type-entity type
}
// Can I view entity with primary key X?
public static bool CanI<TEntity>(Abilities ability, object entityKey)
where TEntity : IStoredEntity
{
TEntity entity = RepositoryProvider.Get<TEntity>().Load(entityKey);
return CanI<TEntity>(ability, entity, AccountRoles.Admin);
}
// Can I view entity (and if I have a specific role, I surely can)?
public static bool CanI<TEntity>(Abilities ability, TEntity entity, params string[] authorizedRoles)
where TEntity : IStoredEntity
{
var principal = Thread.CurrentPrincipal as MyCustomPrincipal;
if (authorizedRoles.Any(r => principal.IsInRole(r)))
return true;
var user = RepositoryProvider.Get<Account, AccountRepository>().GetUser(principal.AccountId);
// my system has only two types of users
if (user is Teacher)
{
return Can<Teacher, TEntity>(user as Teacher, ability, entity);
}
else if (user is TrainingCenter)
{
return Can<TrainingCenter, TEntity>(user as TrainingCenter, ability, entity);
}
return false;
}
/// Can user X (view|edit) entity Y?
/// With some reflection I call the needed method. In this way I can add "abilities" to my ICanAccessQuery
/// interface and its implementations without altering this class.
public static bool Can<TAccount, TEntity>(TAccount user, Abilities ability, TEntity entity)
where TAccount : IAccountOwner
where TEntity : IStoredEntity
{
var key = typeof(TAccount).Name + typeof(TEntity).Name;
if (_authorizations.ContainsKey(key))
{
var query = (ICanAccessQuery<TAccount, TEntity>)_authorizations[key];
string methodName = "Can" + ability.ToString();
var method = typeof(ICanAccessQuery<TAccount, TEntity>).GetMethod(methodName);
return (bool)method.Invoke(query, new object[] { user, entity });
}
return false;
}
}
An example of use in an asp.net mvc controller:
public ActionResult Details(long? id)
{
if (!id.HasValue)
return new EmptyResult();
if (!AuthorizationProvider.CanI<CourseReport>(AuthorizationProvider.Abilities.View, id.Value))
return RedirectToAccessDenied();
// etc.
}

Make a where or any clause in a IRepository pattern with entity framework

I have taken over a project from a collegue, who uses the IRepository pattern. I have never used it before, so I have some problems understanding how to make a WHERE clause or an ANY clause.
Previously I had the following code, which uses the DataContext repository (the actual implementation, where I can use where clauses:
IQueryable<Office> officeList = repository.Offices;
if (specification.CountryId > 0)
{
officeList = repository.Offices.Where(c => c.CountryId == specification.CountryId);
}
if (specification.LetterSize != null)
{
officeList =
officeList.Where(
c => c.OfficeProducts.Any(d => d.OfficeProductDetail.Size == (int)specification.LetterSize));
}
return officeList.ToList();
I would like to understand how to get a code snippet like the one above to work using the IRepository pattern. I have tried to make an implementation of a WHERE/QUERY, but could not get it to work.
My question:
How do you implement a WHERE/ANY statement in practice, so I can do something like the code snippet above?
My IRepository:
public interface IRepository
{
T GetById<T>(long id) where T : class;
void Create<T>(T entity) where T : class;
void Update<T>(T entity) where T : class;
void SaveOrUpdate<T>(T entity) where T : class;
void Delete<T>(T entity) where T : class;
IList<T> FindAll<T>(params OrderBy[] orders) where T : class;
int Count<T>(Expression<Func<T, bool>> whereExpression) where T : class;
bool Exists<T>(Expression<Func<T, bool>> whereExpression) where T : class;
T FindFirst<T>(Expression<Func<T, bool>> whereExpression, params OrderBy[] orders) where T : class;
PaginatedResult<T> Find<T>(Expression<Func<T, bool>> whereExpression, int pageIndex, int pageSize, params OrderBy[] orders) where T : class;
void ExecuteNativeSQL(string sql);
}
The implementation:
public class EFRepository : IRepository
{
private IDBFactory databaseFactory;
private LetterAmazerContext dataContext;
public EFRepository(IDBFactory databaseFactory)
{
this.databaseFactory = databaseFactory;
}
protected LetterAmazerContext DataContext
{
get { return dataContext ?? (dataContext = databaseFactory.Get()); }
}
public T GetById<T>(long id) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
return dbset.Find(id);
}
public void Create<T>(T entity) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
dbset.Add(entity);
}
public void Update<T>(T entity) where T : class
{
dataContext.Entry(entity).State = EntityState.Modified;
}
public void SaveOrUpdate<T>(T entity) where T : class
{
throw new NotImplementedException();
}
public void Delete<T>(T entity) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
dbset.Remove(entity);
}
public IList<T> FindAll<T>(params OrderBy[] orders) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
var query = dbset.Where(t => true);
query = ApplyOrders<T>(query, orders);
return query.ToList<T>();
}
public int Count<T>(Expression<Func<T, bool>> whereExpression) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
return dbset.Count<T>(whereExpression);
}
public bool Exists<T>(Expression<Func<T, bool>> whereExpression) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
return dbset.Count<T>(whereExpression) != 0;
}
public T FindFirst<T>(Expression<Func<T, bool>> whereExpression, params OrderBy[] orders) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
var query = dbset.Where(whereExpression);
query = ApplyOrders<T>(query, orders);
return query.SingleOrDefault<T>();
}
public PaginatedResult<T> Find<T>(Expression<Func<T, bool>> whereExpression, int pageIndex, int pageSize, params OrderBy[] orders) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
PaginatedResult<T> results = new PaginatedResult<T>();
var query = dbset.AsExpandable().Where(whereExpression);
query = ApplyOrders<T>(query, orders);
results.Results = query.Skip<T>(pageIndex * pageSize).Take<T>(pageSize).ToList<T>();
results.TotalItems = dbset.AsExpandable().LongCount(whereExpression);
return results;
}
public void ExecuteNativeSQL(string sql)
{
DataContext.Database.ExecuteSqlCommand(sql);
}
private IQueryable<T> ApplyOrders<T>(IQueryable<T> query, params OrderBy[] orders)
{
if (orders == null || orders.Length == 0) return query;
foreach (var order in orders)
{
query = query.OrderBy(order.ToString());
}
return query;
}
}
Your repository is at the moment open for arbitrary expressions, including potentially expressions that cannot be evaluated by the implementation.
On one hand this is a potential risk of not being able to deliver an implementation that matches so open contract.
On the other hand, why don't you just then expose it a bit more:
public interface IRepository
{
...
IQueryable<T> Query<T>();
}
and
public class EFRepository : IRepository
{
...
public IQueryable<T> Query<T>()
{
return DataContrxt.Set<T>();
}
}
Note that if you decide to go that way, most of your specific query methods doesn't make sense anymore as such open generic queryable interface satisfies most needs.
Note also that the same concern applies there, if you have multiple implementations you can't possibly guarantee that the contract is satisfied in the same way. Also, your potential client can easily create a query the provider will reject. If you accept these issues, the proposed solution solves your issue as you can now query the repository for almost anything.
And the last note, if you don't plan to have multiple implementations, remove the repository layer completely. The EF context is a unit of work with repositories inside. Starting from version 6, the context can be mocked so unit testing is possible.

Categories

Resources