Может ли DbContext применять политику фильтрации?

Я хотел бы передать значение для ctor DbContext, а затем использовать это значение для «фильтрации» соответствующих DbSets. Возможно ли это … или есть лучший подход?

Код может выглядеть так:

class Contact { int ContactId { get; set; } int CompanyId { get; set; } string Name { get; set; } } class ContactContext : DbContext { public ContactContext(int companyId) {...} public DbSet 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, чтобы справиться с этим. Чтобы использовать этот class, вы передаете 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 : IDbSet, IOrderedQueryable, IOrderedQueryable, IQueryable, IQueryable, IEnumerable, IEnumerable, IListSource where TEntity : class { private readonly DbSet Set; private readonly IQueryable FilteredSet; private readonly Action InitializeEntity; public FilteredDbSet(DbContext context) : this(context.Set(), i => true, null) { } public FilteredDbSet(DbContext context, Expression> filter) : this(context.Set(), filter, null) { } public FilteredDbSet(DbContext context, Expression> filter, Action initializeEntity) : this(context.Set(), filter, initializeEntity) { } private FilteredDbSet(DbSet set, Expression> filter, Action initializeEntity) { Set = set; FilteredSet = set.Where(filter); MatchesFilter = filter.Compile(); InitializeEntity = initializeEntity; } public Func 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() where TDerivedEntity : class, TEntity { var entity = Set.Create(); 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); } ///  /// Returns the items in the local cache ///  ///  /// It is possible to add/remove entities via this property that do NOT match the filter. /// Use the  method before adding/removing an item from this collection. ///  public ObservableCollection Local { get { return Set.Local; } } IEnumerator IEnumerable.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, поэтому, вероятно, нет способа включить пользовательское условие.

Для этого потребуется некоторая shell, которая будет обрабатывать эти требования (может быть repository):

  • Условие в select можно обрабатывать методом обтекания вокруг DbSet, который добавит условие Where
  • Вставка может обрабатываться также методом упаковки
  • Обновление должно обрабатываться путем переопределения SaveChanges и использования context.ChangeTracker для получения всех обновленных объектов. Затем вы можете проверить, были ли сущности изменены.

По обертке я не имею в виду пользовательскую реализацию DbSet – это слишком сложно:

 public class MyDal { private DbSet _set; public MyDal(DbContext context) { _set = context.Set(); } public IQueryable GetQuery() { return _set.Where(e => ...); } // Attach, Insert, Delete } 
  • Единичное тестирование DbContext
  • Преобразование DBContext в ObjectContext для использования с GridView
  • Как отключить объекты в Entity Framework Code First?
  • Code First - лучше ли использовать annotations данных или Fluent API?
  • Не удалось выполнить проверку для одного или нескольких объектов. Дополнительную информацию см. В разделе «Свойство EntityValidationErrors».
  • Как настроить отношение многих ко многим, используя API-интерфейс сущностей
  • Entity Framework CodeFirst многие-многие отношения с дополнительной информацией
  • Как вы гарантируете, что Cascade Delete включена в отношении отношения таблицы в EF Code?
  • Навигационное свойство без объявления внешнего ключа
  • Entity Framework объединяет 3 таблицы
  • Entity Framework 4.1 Атрибут InverseProperty и ForeignKey
  • Давайте будем гением компьютера.