Entity Framework Code First – Applying Global Filters

You may also like...

  • bloparod

    Great post! It works in most of the cases.

    However, I have this error for some queries (added with .Where(…) to a FilteredDbSet:

    The error is “Unable to create a constant value of type [entity type] … Only primitive types or enumeration types are supported in this context”.

    The queries worked on DbSet. Sometimes, I can rewrite the query and fix the error, but I expected not to have to change the context clients.

    ¿Any thoughts about this? Thanks!

  • No need to filter at controller action level you can simply put that inside Context constructor and it will work…….

    public CustomerContext()
    : base(“DefaultConnection”)
    {
    this.ApplyFilters(new List<IFilter>()
    {
    new GeneratorBase.MVC.Models.CustomerContext.ActiveCustomersFilter()
    });
    }

  • So, I was trying to use this. I think it is a very useful method of filtering but it seems that the code presented doesnt work when chaining multiple filters. I had in my app a need for filtering based on EmployeeId. So I set up a filter for the entities I needed to filter and everything worked as expected. Then I added a filter for NotDeleted, which, as you may guess, filtered based on a IsDeleted bool in the entities. The problem is, it seems that the last filter wins.

    The reason for this is simple. In each filter the DbContext is passed into the FilteredDbSet. The constructor for the FilteredDbSet get the DbSet by context.Set() and passes that to the private constructor. Here is the issue: the DbContext Set will always be unfiltered. So the second filter in the chain just filters the original values.

    So now the fix:
    Its pretty easy, I changed the:
    private readonly DbSet _set;
    to
    private readonly IDbSet _set;

    the
    private FilteredDbSet(DbSet set, Expression<Func> filter, Action initializeEntity)
    to
    private FilteredDbSet(IDbSet set, Expression<Func> filter, Action initializeEntity)

    and added a public constructor that takes an IDbSet instead of DbContext
    public FilteredDbSet(IDbSet set, Expression<Func> filter)
    : this(set, filter, null)
    {
    }

    So, this way in the filter instead of passing in the DbContext as a parameter, pass in DbContext.TEntity, this way a previously filtered Set will not lose its filter.

    One side effect though, the method SqlQuery on FilteredDbSet will not work as IDbSet doesnt expose this. I just commented the whole method out, someone else might do some more work to preserve its function, but I feel if Im going to run a raw sql query it will be a) an edge case and b) I can manually filter in the sql statement.

    Hope this helps someone.

    Jason

    • Hi Jason,
      Thanks a lot for this. Would you be able to implement this in the example given in this post (zipped file) and send it to me. I would then expose it and share to others?

  • So far, nothing. I’m working at this too. It’s sad EF doesn’t support native soft delete.

  • Bart Geyskens

    Nice code, I almost thought that it was solving my problem.

    But it isn’t. There is problem with this kind of filtering because of the include.

    The filters that you apply are
    DbContext.Products = new FilteredDbSet(DbContext, d => d.IsActive && d.Category.IsActive);
    DbContext.Categories = new FilteredDbSet(DbContext, c => c.Products.All(p => p.IsActive));

    But, the filter on category will also exclude categories which have a mix of active / not active products.
    In reality, such a mix is often the case.

    When you remove the categoy filter then suddenly those inactive products are back when retrieving categories, including products

    context.Categories.Include(c => c.Products).ToList();

    Do you have a solution for that problem ?

    • Hi Bart,
      I will take a look on this as it seems a serious flaw.

      • Kamil

        Has this problem ever been resolved?

        • Anyone ever get this working? I was just writing a reply explaining the same thing when I realized someone already posted it. The query stated:

          var categories = context.Categories.Include(“Products”).ToList();

          returns only categories that have all active products, excluding categories that have both active and inactive products.

          How do we return all categories, along with only their active products?

      • Zachary Scott

        context.Categories.Include(c => c.Products).ToList(); translates to a left join so all of Categories should be present and only Products that match those categories and null values for products that do not match. So the filter might just need to allow null values?

  • Curt

    Regarding the text at the top of this article: “POCO object, which fits perfectly with my programming style, as I try to follow as much as possible Domain Driven Development”, Entity Framework’s implementation of POCO is not domain-driven or agile given the following constraints:

    • The class and property names must align with the Entity Data Model.

    • All Entity Data Model properties in the model entity must be represented in the class.

    • Hi Curt,
      Thanks for commenting.
      Actually none of your claims are true, as if you check the Entity Framework Code First version of the framework (which i usually refer to), EF Code First perfectly fits with the POCO definitions.

      I agree with you that this was not the case with previous versions of EF, where one would be forced to use the designer. With EF Code First you can configure your existing domain objects to map to your database. Yes, there are some limitations as it doesn’t support all of the scenarios that NHibernate for instance would support, but yet most of the scenarios are possible. So, your class names do not need to be called in a specific way, not the class properties need to follow any pattern.

      I invite you to read some documents around Entity Framework Code First.

      Cheers,
      Zoran

  • What’s about implementation without having to explicitly define IDbSet. Let say that the system use directly the Set() method to get the Entity. Is there a way to implement the filter?

    • Hi Patrick,
      For what am I aware of, it is not possible to create a global filter in such a way. It is possible to introduce a where clause for a given entity, but the whole catch is when you start using the Include method, and at that point without the code above your filter won’t work.
      Please let me know if in the meantime you found something as I would be interested in the solution!

  • Definitely, what a splendid site and instructive posts, I definitely will bookmark your blog.Best Regards!

mapes_mariah@mailxu.com