Qaiser Iftikhar
Qaiser Iftikhar

Reputation: 495

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 (aps.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?

Upvotes: 2

Views: 2487

Answers (1)

Greg Smith
Greg Smith

Reputation: 2469

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.

Upvotes: 1

Related Questions