如何解决DbContext可以强制执行过滤器策略吗?
| 我想将一个值传递给DbContext的ctor,然后将该值在相关的DbSet上强制执行\“ filtering \”。这可能吗?还是有更好的方法? 代码可能如下所示: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;
}
解决方法
我决定实现自定义IDbSet来处理此问题。要使用此类,您需要传入DbContext,过滤器表达式和(可选)Action来初始化新实体,使其符合过滤条件。
我已经测试了枚举集合并使用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,bool>> filter,Action<TEntity> initializeEntity)
: this(context.Set<TEntity>(),initializeEntity)
{
}
private FilteredDbSet(DbSet<TEntity> set,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);
}
}
}
,EF没有任何“过滤器”功能。您可以通过继承自定义DbSet
来达到类似的目的,但是我认为这仍然是有问题的。例如,DbSet直接实现IQueryable,因此可能没有办法包括自定义条件。
这将需要一些包装程序来处理这些要求(可以是存储库):
select中的条件可以通过在DbSet周围包装方法来处理,这将添加Where
条件
插入也可以通过包装方法处理
更新必须通过覆盖SaveChanges
并使用ѭ5handled来获取所有更新的实体。然后,您可以检查实体是否被修改。
包装器我不是自定义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
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。