C# DbContext能否强制实施筛选策略?

C# DbContext能否强制实施筛选策略?,c#,entity-framework-4.1,C#,Entity Framework 4.1,我想将一个值传递给DbContext的ctor,然后让该值在相关的dbset上强制执行“过滤”。这是可能的……还是有更好的方法 代码可能如下所示: class Contact { int ContactId { get; set; } int CompanyId { get; set; } string Name { get; set; } } class ContactContext : DbContext { public ContactContext(int compan

我想将一个值传递给DbContext的ctor,然后让该值在相关的dbset上强制执行“过滤”。这是可能的……还是有更好的方法

代码可能如下所示:

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;
}
class联系人{
int ContactId{get;set;}
int CompanyId{get;set;}
字符串名称{get;set;}
}
类ContactContext:DbContext{
公共联系人上下文(int companyId){…}
公共数据库集联系人{get;set;}
}
使用(var cc=new ContactContext(123)){
//仅返回CompanyId=123的联系人
var all=(从cc.Contacts中的i选择i);
//将自动将CompanyId设置为123
var contact=新联系人{Name=“Doug”};
cc.Contacts.Add(联系人);
cc.SaveChanges();
//将引发自定义异常
contact.CompanyId=456;
cc.1.1变更;
}
EF没有任何“过滤器”功能。您可以通过继承custom
DbSet
来尝试实现类似的功能,但我认为这仍然会有问题。例如,DbSet直接实现IQueryable,因此可能无法包含自定义条件

这将需要一些包装器来处理这些需求(可以是存储库):

  • select中的条件可以通过环绕DbSet的包装方法来处理,该方法将添加
    Where
    条件
  • 插入也可以通过包装方法处理
  • 必须通过覆盖
    SaveChanges
    并使用
    context.ChangeTracker
    获取所有更新的实体来处理更新。然后可以检查是否修改了实体
我所说的包装器并不是指定制
DbSet
实现——这太复杂了:

public class MyDal
{
    private DbSet<MyEntity> _set;

    public MyDal(DbContext context)
    {
        _set = context.Set<MyEntity>();
    }

    public IQueryable<MyEntity> GetQuery()
    {
        return _set.Where(e => ...);
    }

    // Attach, Insert, Delete
}
公共类MyDal
{
专用数据库集;
公共MyDal(DbContext)
{
_set=context.set();
}
公共IQueryable GetQuery()
{
返回集合,其中(e=>…);
}
//附加、插入、删除
}

我决定实现一个自定义IDbSet来处理这个问题。要使用此类,您需要传入一个DbContext、一个筛选器表达式和(可选)一个操作来初始化新实体,使它们满足筛选器条件

我已经测试了枚举集合和使用Count聚合函数。它们都会修改生成的SQL,因此它们应该比在客户机上进行过滤更有效

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);
        }
    }
}
使用系统;
使用系统集合;
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用系统组件模型;
使用System.Data.Entity;
使用System.Linq;
使用System.Linq.Expressions;
名称空间makeMyCredit.Data
{
类FilteredDbSet:IDbSet、IOrderedQueryable、IOrderedQueryable、IQueryable、IQueryable、IEnumerable、IEnumerable、IListSource
地点:班级
{
专用只读数据库集;
专用只读可读取筛选器集;
私有只读操作初始化;
公共筛选器DDBSET(DbContext上下文)
:this(context.Set(),i=>true,null)
{
}
公共筛选器DDBSET(DbContext上下文、表达式筛选器)
:此(context.Set(),筛选器,null)
{
}
公共筛选器DDBSET(DbContext上下文、表达式筛选器、操作初始化属性)
:此(context.Set()、筛选器、initializeEntity)
{
}
私有过滤器RedBSET(数据库集、表达式过滤器、操作初始化属性)
{
集合=集合;
FilteredSet=设置,其中(过滤器);
MatchesFilter=filter.Compile();
InitializeEntity=InitializeEntity;
}
public Func MatchesFilter{get;private set;}
通过FentityDoesnotmatchFilter(TEntity实体)公开作废
{
如果(!MatchesFilter(实体))
抛出新ArgumentOutOfRangeException();
}
公共TEntity Add(TEntity实体)
{
DoInitializeEntity(实体);
THROWIFENTITYDOESNOTMACHFILTER(实体);
返回集合。添加(实体);
}
公共临时附属机构(临时实体)
{
THROWIFENTITYDOESNOTMACHFILTER(实体);
返回集合。附加(实体);
}
public TDerivedEntity Create(),其中TDerivedEntity:class,TEntity
{
var entity=Set.Create();
DoInitializeEntity(实体);
返回(TDerivedEntity)实体;
}
公共关系(创建)
{
var entity=Set.Create();
DoInitializeEntity(实体);
返回实体;
}
public TEntity Find(参数对象[]键值)
{
var entity=Set.Find(键值);
if(实体==null)
返回null;
//如果用户查询了筛选器之外的项目,那么我们将抛出一个错误。
//如果IDbSet有一个Detach方法,我们会使用它……遗憾的是,我们必须对集合中的项保持一致。
THROWIFENTITYDOESNOTMACHFILTER(实体);
返回实体;
}
公共TEntity移除(TEntity实体)
{
THROWIFENTITYDOESNOTMACHFILTER(实体);
返回集合。删除(实体);
}
/// 
///返回本地缓存中的项
/// 
/// 
///可以通过此属性添加/删除与筛选器不匹配的实体。
///在此集合中添加/删除项之前,请使用此方法。
/// 
公共observeCollection Local{get{return Set.Local;}}
IEnumerator IEnumerable.GetEnumerator(){return FilteredSet.GetEnumerator();}
IEnumerator IEnumerable.GetEnumerator(){return FilteredSet.GetEnumerator();}
类型IQueryable.ElementType{get{return typeof(tenty);}
表达式IQueryable.Expression{get{return FilteredSet.Expression;}
IQueryProvider IQueryable.Provider{get{return FilteredSet.Provider;}}