一个的DbContext可以实施过滤策略? [英] Can a DbContext enforce a filter policy?

查看:123
本文介绍了一个的DbContext可以实施过滤策略?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想传递一个值到的DbContext的构造函数,然后有这个值执行过滤的相关DbSets。这是可能的...或者有没有更好的方法呢?

code可能是这样的:

 类联系{
  INT的ContactID {搞定;组; }
  INT CompanyId {搞定;组; }
  字符串名称{;组; }
}类ContactContext:{的DbContext
  公共ContactContext(INT companyId){...}
  公共DbSet<联系与GT;联系方式{搞定;组; }
}使用(VAR CC =新ContactContext(123)){
  //仅会返回接触,其中CompanyId = 123
  VAR所有=(在cc.Contacts从我选择我);  //会自动设置CompanyId 123
  VAR接触=新的联系人{名称=道};
  cc.Contacts.Add(接触);
  cc.SaveChanges();  //将引发自定义异常
  contact.CompanyId = 456;
  cc.SaveChanges;
}


解决方案

我决定实现一个自定义IDbSet来处理这个问题。要使用这个类,你传递一个的DbContext,过滤器前pression和(可选),这样他们符合过滤条件的动作来初始化新的实体。

我测试过枚举集,并使用Count聚​​合函数。两者修改所生成的SQL所以他们应该要比在客户端上的过滤更有效。

 使用系统;
System.Collections中使用;
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.ComponentModel;
使用System.Data.Entity的;
使用System.Linq的;
使用System.Linq.Ex pressions;
命名空间MakeMyPledge.Data
{
    类FilteredDbSet< TEntity> :IDbSet< TEntity>中IOrderedQueryable< TEntity>中IOrderedQueryable,IQueryable的< TEntity>中的IQueryable,IEnumerable的< TEntity>中的IEnumerable,IListSource
        其中,TEntity:类
    {
        私人只读DbSet< TEntity>组;
        私人只读的IQueryable< TEntity> FilteredSet;
        私人只读动作< TEntity> InitializeEntity;        公共FilteredDbSet(的DbContext上下文)
            :这(context.Set< TEntity>(),I =>真,NULL)
        {
        }        公共FilteredDbSet(的DbContext的背景下,前pression<&Func键LT; TEntity,布尔>>过滤器)
            :这(context.Set< TEntity>(),过滤器,空)
        {
        }        公共FilteredDbSet(的DbContext的背景下,前pression<&Func键LT; TEntity,布尔>>过滤器,动作< TEntity> initializeEntity)
            :这(context.Set< TEntity>(),过滤器,initializeEntity)
        {
        }        私人FilteredDbSet(DbSet< TEntity>设置,防爆pression<&Func键LT; TEntity,布尔>>过滤器,动作< TEntity> initializeEntity)
        {
            设置=集;
            FilteredSet = set.Where(过滤器);
            MatchesFilter = filter.Compile();
            InitializeEntity = initializeEntity;
        }        公共Func键< TEntity,布尔> MatchesFilter {搞定;私人集; }        公共无效ThrowIfEntityDoesNotMatchFilter(TEntity实体)
        {
            如果(!MatchesFilter(实体))
                抛出新ArgumentOutOfRangeException();
        }        公共TEntity添加(TEntity实体)
        {
            DoInitializeEntity(实体);
            ThrowIfEntityDoesNotMatchFilter(实体);
            返回Set.Add(实体);
        }        公共TEntity连接(TEntity实体)
        {
            ThrowIfEntityDoesNotMatchFilter(实体);
            返回Set.Attach(实体);
        }        公共TDerivedEntity创建< TDerivedEntity>()其中TDerivedEntity:类,TEntity
        {
            VAR实体= Set.Create< TDerivedEntity>();
            DoInitializeEntity(实体);
            返回(TDerivedEntity)实体;
        }        公共TEntity的Create()
        {
            变种实体= Set.Create();
            DoInitializeEntity(实体);
            返回实体;
        }        公共TEntity查找(params对象[]键值)
        {
            VAR实体= Set.Find(键值);
            如果(实体== NULL)
                返回null;            //如果用户查询的过滤器以外的项目,然后我们抛出一个错误。
            //如果IDbSet了,我们将使用它的方法分离......可悲的是,我们必须确定在集合的项目之中。
            ThrowIfEntityDoesNotMatchFilter(实体);
            返回实体;
        }        公共TEntity删除(TEntity实体)
        {
            ThrowIfEntityDoesNotMatchFilter(实体);
            返回Set.Remove(实体);
        }        ///<总结>
        ///返回在本地缓存中的项目
        ///< /总结>
        ///<&言论GT;
        ///它可以通过这个属性不匹配过滤器的添加/删除实体。
        ///使用的<见CREF =ThrowIfEntityDoesNotMatchFilter/>添加/从此集合删除项目之前的方法。
        ///< /言论>
        公众的ObservableCollection< TEntity>当地{{返回Set.Local; }}        IEnumerator的< TEntity> IEnumerable的< TEntity> .GetEnumerator(){返回FilteredSet.GetEnumerator(); }        的IEnumerator IEnumerable.GetEnumerator(){返回FilteredSet.GetEnumerator(); }        键入IQueryable.ElementType {{返回的typeof(TEntity)已; }}        防爆pression IQueryable.Ex $ P $ {pssion获得{返回FilteredSet.Ex pression; }}        IQueryProvider IQueryable.Provider {{返回FilteredSet.Provider; }}        布尔IListSource.ContainsListCollection {{返回FALSE; }}        IList的IListSource.GetList(){抛出新的InvalidOperationException异常(); }        无效DoInitializeEntity(TEntity实体)
        {
            如果(InitializeEntity!= NULL)
                InitializeEntity(实体);
        }
    }
}

I would like to pass a value to the ctor of a DbContext and then have that value enforce "filtering" on the related DbSets. Is this possible...or is there a better approach?

Code might look like this:

class Contact {
  int ContactId { get; set; }
  int CompanyId { get; set; }
  string Name { get; set; }
}

class ContactContext : DbContext {
  public ContactContext(int companyId) {...}
  public DbSet<Contact> Contacts { get; set; }
}

using (var cc = new ContactContext(123)) {
  // Would only return contacts where CompanyId = 123
  var all = (from i in cc.Contacts select i);

  // Would automatically set the CompanyId to 123
  var contact = new Contact { Name = "Doug" };
  cc.Contacts.Add(contact);
  cc.SaveChanges();

  // Would throw custom exception
  contact.CompanyId = 456;
  cc.SaveChanges;
}

解决方案

I decided to implement a custom IDbSet to deal with this. To use this class, you pass in a DbContext, a filter expression, and (optionally) an Action to initialize new entities so they meet the filter criteria.

I've tested enumerating the set and using the Count aggregate functions. Both of them modify the SQL that is generated so they should be much more efficient than filtering on the client.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;


namespace MakeMyPledge.Data
{
    class FilteredDbSet<TEntity> : IDbSet<TEntity>, IOrderedQueryable<TEntity>, IOrderedQueryable, IQueryable<TEntity>, IQueryable, IEnumerable<TEntity>, IEnumerable, IListSource
        where TEntity : class
    {
        private readonly DbSet<TEntity> Set;
        private readonly IQueryable<TEntity> FilteredSet;
        private readonly Action<TEntity> InitializeEntity;

        public FilteredDbSet(DbContext context)
            : this(context.Set<TEntity>(), i => true, null)
        {
        }

        public FilteredDbSet(DbContext context, Expression<Func<TEntity, bool>> filter)
            : this(context.Set<TEntity>(), filter, null)
        {
        }

        public FilteredDbSet(DbContext context, Expression<Func<TEntity, bool>> filter, Action<TEntity> initializeEntity)
            : this(context.Set<TEntity>(), filter, initializeEntity)
        {
        }

        private FilteredDbSet(DbSet<TEntity> set, Expression<Func<TEntity, bool>> filter, Action<TEntity> initializeEntity)
        {
            Set = set;
            FilteredSet = set.Where(filter);
            MatchesFilter = filter.Compile();
            InitializeEntity = initializeEntity;
        }

        public Func<TEntity, bool> MatchesFilter { get; private set; }

        public void ThrowIfEntityDoesNotMatchFilter(TEntity entity)
        {
            if (!MatchesFilter(entity))
                throw new ArgumentOutOfRangeException();
        }

        public TEntity Add(TEntity entity)
        {
            DoInitializeEntity(entity);
            ThrowIfEntityDoesNotMatchFilter(entity);
            return Set.Add(entity);
        }

        public TEntity Attach(TEntity entity)
        {
            ThrowIfEntityDoesNotMatchFilter(entity);
            return Set.Attach(entity);
        }

        public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, TEntity
        {
            var entity = Set.Create<TDerivedEntity>();
            DoInitializeEntity(entity);
            return (TDerivedEntity)entity;
        }

        public TEntity Create()
        {
            var entity = Set.Create();
            DoInitializeEntity(entity);
            return entity;
        }

        public TEntity Find(params object[] keyValues)
        {
            var entity = Set.Find(keyValues);
            if (entity == null)
                return null;

            // If the user queried an item outside the filter, then we throw an error.
            // If IDbSet had a Detach method we would use it...sadly, we have to be ok with the item being in the Set.
            ThrowIfEntityDoesNotMatchFilter(entity);
            return entity;
        }

        public TEntity Remove(TEntity entity)
        {
            ThrowIfEntityDoesNotMatchFilter(entity);
            return Set.Remove(entity);
        }

        /// <summary>
        /// Returns the items in the local cache
        /// </summary>
        /// <remarks>
        /// It is possible to add/remove entities via this property that do NOT match the filter.
        /// Use the <see cref="ThrowIfEntityDoesNotMatchFilter"/> method before adding/removing an item from this collection.
        /// </remarks>
        public ObservableCollection<TEntity> Local { get { return Set.Local; } }

        IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator() { return FilteredSet.GetEnumerator(); }

        IEnumerator IEnumerable.GetEnumerator() { return FilteredSet.GetEnumerator(); }

        Type IQueryable.ElementType { get { return typeof(TEntity); } }

        Expression IQueryable.Expression { get { return FilteredSet.Expression; } }

        IQueryProvider IQueryable.Provider { get { return FilteredSet.Provider; } }

        bool IListSource.ContainsListCollection { get { return false; } }

        IList IListSource.GetList() { throw new InvalidOperationException(); }

        void DoInitializeEntity(TEntity entity)
        {
            if (InitializeEntity != null)
                InitializeEntity(entity);
        }
    }
}

这篇关于一个的DbContext可以实施过滤策略?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆