asp.net-mvcentity-frameworkef-code-first

Entity framework add a where clause to all queries


I am using Entity framework 5 and using repository pattern. Say I got these entities Customer, Files, Images, Tasks, Invoice, User.

Each entity (apart from Customer) has a foreign key of Customer. When a user logs in I store the customerid in session (ASP.NET MVC). What I want is any CRUD taken on all entities to be limited to the customer who's user is logged in. e.g I can't afford to delete a Task belonging to customer 1 to be deleted by user who is from customer 2.

Is adding an argument of customerid for each method of repositories the best way to achieve this or are there any better/clever ways of doing it?


Solution

  • Tricky to give a definitive answer but you could make it a bit more extensible by implementing higer order functions, like this:

    public interface IRepository<T>
    {
        public T GetBy(Expression<Func<T, bool>> query)
    }
    
    
    public class FileRepository : IRepository<File>
    {
        public File GetBy(Expression<Func<T, bool>> query)
        {
            using(var context = new FilesContext())
            {
                return context.Files.Where(query).FirstOrDefault();
            }
        }
    
    }
    
    public class SomeController
    {
        private IRepository<File> _repo;
    
        public SomeController(IRepository<File> repo)
        {
            _repo = repo;
        }
    
       public ActionResult Index()
       {
           var model = _repo.GetBy(f => f.CustomerId == Session.Whatever.CustomerId);
    
           return View(model);
       }
    
    }
    

    This way you can vary the search query when required, rather than tie yourself in to using a hardcoded customer id property. For example, if you wanted to get the File object by the FileID, not the CustomerID, then:

    var model = _repo.GetBy(f => f.FileId == someId);
    

    and that's the only part of the code that needs to change.

    Some really good info on Higher Order functions and functional programming in C# here: http://www.codeproject.com/Articles/375166/Functional-programming-in-Csharp

    Edit:

    You might be able to isolate the "Always use the customer ID when hitting DB" into a repository of it's own, using a decorator style pattern, thus: (massive disclaimer - I haven't tested this, but something along these lines should work)

    public class SpecialFileRepo : IRepository<File>
    {
        private readonly IRepository<File> _baseRepo;
    
        public SpecialFileRepo(IRepository<File> baseRepo)
        {
            _baseRepo = baseRepo;
        }
    
        public SpecialFileRepo() : this(new FileRepository())
        {
    
        }
        public File GetBy(Expression<Func<File, bool>> query)
        {
            var parameters = query.Parameters;
            var newParam = Expression.Parameter(typeof (File), "f");
    
            var additionalQuery = Expression.AndAlso(query.Body,
                                                     Expression.Equal(
                                                         Expression.PropertyOrField(newParam, "CustomerId"),
                                                         Expression.Constant(HttpContext.Current.Session["customerId"])));
    
            var newQuery = query.Update(additionalQuery, parameters);
    
    
            return _baseRepo.GetBy(newQuery);
        }
    }
    

    Then anything that's talking to a repository, as far as it's concerned, it's just a base repository, but this class is sitting in between and always grafting the "customerid = sessionwhatever" expression onto what finally gets passed to the database. And of course, anything that only cares about using the base repository, can still do so.