Finbuckle / Finbuckle.MultiTenant

Finbuckle.MultiTenant is an open-source multitenancy middleware library for .NET. It enables tenant resolution, per-tenant app behavior, and per-tenant data isolation.
https://www.finbuckle.com/multitenant
Apache License 2.0
1.32k stars 267 forks source link

MultiTenantDbContext and other global query filters #151

Closed nbarbettini closed 5 years ago

nbarbettini commented 5 years ago

I like the fact that deriving from MultiTenantDbContext automatically applies a global query filter for TenantId without me thinking about it. However, there is currently no way to support additional query filters.

I have a multitenant app that also uses the "soft delete" pattern (the other classic example of a global query filter). What do you think would be a good way to allow additional query filters plus the default TenantId filter?

I'm happy to contribute a PR, just want to get your thoughts first.

nbarbettini commented 5 years ago

What I'm currently doing is:

Adding a virtual method to MultiTenantDbContext:

        protected virtual BinaryExpression GetAdditionalFilter(Type entityType, ParameterExpression entityParameter)
        {
            // A derived class may override this
            return null;
        }

Overriding it in my application's DbContext:

        protected override BinaryExpression GetAdditionalFilter(Type entityType, ParameterExpression entityParameter)
        {
            if (!DeletableTypes.Contains(entityType))
            {
                return null;
            }

            // build expression tree for e.Deleted == false
            // e.Deleted
            var propertyExp = Expression.Property(entityParameter, nameof(IDeletable.Deleted));

            // e.Deleted == false
            var equalsFalseExp = Expression.Equal(propertyExp, Expression.Constant(false));

            return equalsFalseExp;
        }

And doing some expression stuff in Shared to combine the result of GetAdditionalFilter (if it's not null) with the TenantId filter Finbuckle is already building.

AndrewTriesToCode commented 5 years ago

Hi @nbarbettini I agree that there needs to be better support for multiple query filters. I'm going to take a closer look at it (and your approach) this week.

Without the code in front of me my first thought would be to see if a derived could add a global query filter in the OnModelCreating method before calling base.OnModelCreating then Finbuckle.MultiTenant could somehow detect the existing query and wrap its tenant id query around the existing one.

AndrewTriesToCode commented 5 years ago

Hi @nbarbettini , I put together something quick based on my thoughts above. What do you think of PR #153

Now all you have to do is add your global query as usual, but make sure it is before you call base.OnModelCreating. I plan to update the documentation to mention that as well later this week.

nbarbettini commented 5 years ago

Thanks, looks awesome! 🎉