Dou*_*ter 31 c# entity-framework-4.1
我想将值传递给DbContext的ctor,然后让该值对相关的DbSets强制执行"过滤".这可能......还是有更好的方法?
代码可能如下所示:
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;
}
Run Code Online (Sandbox Code Playgroud)
Dou*_*ter 47
我决定实现一个自定义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, 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
EF没有任何"过滤"功能.你可以尝试通过继承自定义来实现这样的东西,DbSet但我认为它仍然会有问题.例如,DbSet直接实现IQueryable,因此可能无法包含自定义条件.
这将需要一些将处理这些要求的包装器(可以是存储库):
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
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11958 次 |
| 最近记录: |