C# DbContext能否强制实施筛选策略?
我想将一个值传递给DbContext的ctor,然后让该值在相关的dbset上强制执行“过滤”。这是可能的……还是有更好的方法 代码可能如下所示: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
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没有任何“过滤器”功能。您可以通过继承customDbSet
来尝试实现类似的功能,但我认为这仍然会有问题。例如,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;}}