我可以在选择时让我的实体框架DbSet调用我的表值函数吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我可以在选择时让我的实体框架DbSet调用我的表值函数吗?相关的知识,希望对你有一定的参考价值。

我有许多使用DbSet<dbo_Deal>的现有查询,现在要求过滤未经授权用户的机密交易。我想知道是否有办法覆盖DbSet<dbo_Deal>,以便它选择使用Table Valued参数而不是默认行为。

我创建了以下TVF,如果用户没有访问权限,则过滤掉机密交易:

CREATE FUNCTION [dbo].[GetDeals](@UserKey int)
RETURNS TABLE
RETURN (
    SELECT d.*
    FROM dbo.Deal d
    WHERE d.Confidentiality = 0
    OR EXISTS(SELECT *
                FROM dbo.UserRole ur
                WHERE ur.UserKey = @UserKey
                AND ur.Role = 'Admin')
);

我还在我的DbContext中添加了以下内容来调用SQL函数:

[DbFunction("MyDbContext", "GetDeals")]
[CodeFirstStoreFunctions.DbFunctionDetails(DatabaseSchema = "dbo")]
public IQueryable<dbo_Deal> GetDeals()
{
    var userKeyParam = new System.Data.Entity.Core.Objects.ObjectParameter("UserKey", typeof(int)) { Value = _userKey };
    return ((System.Data.Entity.Infrastructure.IObjectContextAdapter)this).ObjectContext.CreateQuery<dbo_Deal>("[MyDbContext].[GetDeals](@UserKey)", userKeyParam);
}

我知道我可以重构我的所有查询来调用这个函数,但是如果我能以某种方式指示Entity Framework在选择或加入Deals时使用这个函数会很棒。那可能吗?

答案

尝试Moq你的DbSet

public class YourContext: DbContext
{
    public YourContext()
    {
        var tvf = GetDeals();

        var mockSet = new Mock<DbSet<dbo_Deal>>();      

        mockSet.As<IQueryable<dbo_Deal>>().Setup(m => m.Provider).Returns(tvf.Provider);
        mockSet.As<IQueryable<dbo_Deal>>().Setup(m => m.Expression).Returns(tvf.Expression);
        mockSet.As<IQueryable<dbo_Deal>>().Setup(m => m.ElementType).Returns(tvf.ElementType);
        mockSet.As<IQueryable<dbo_Deal>>().Setup(m => m.GetEnumerator()).Returns(() => tvf.GetEnumerator());        

        //your DbSet:
        dbo_Deals = mockSet.Object;
    }   
}
另一答案

我无法按照我想要的方式使用SQL函数获得解决方案,所以我使用了包含DbSet的FilteredDbSet。我必须做的就是在DbContext中使用FilteredDbSet在我的属性上创建返回类型,然后使用我想要的过滤器在构造函数中实例化它。我还在下面公开的类中创建了私有构造函数,因此我可以模拟它进行单元测试。

这最终成为一个非常好的解决方案,因为我避免重构所有现有的Linq查询,任何未来的查询将自动得到这种行为。

public class FilteredDbSet<TEntity> : IDbSet<TEntity>, IOrderedQueryable<TEntity>, IListSource where TEntity : class
{
  private readonly DbSet<TEntity> _set;
  private readonly Action<TEntity> _initializeEntity;
  private readonly Expression<Func<TEntity, bool>> _filter;

  public FilteredDbSet(DbContext context, Expression<Func<TEntity, bool>> filter, Action<TEntity> initializeEntity)
    : this(context.Set<TEntity>(), filter, initializeEntity)
  {
  }

  public IQueryable<TEntity> Include(string path)
  {
    return _set.Include(path).Where(_filter).AsQueryable();
  }

  private FilteredDbSet(DbSet<TEntity> set, Expression<Func<TEntity, bool>> filter, Action<TEntity> initializeEntity)
  {
    _set = set;
    _filter = filter;
    _initializeEntity = initializeEntity;
  }

  public IQueryable<TEntity> Unfiltered()
  {
    return _set;
  }

  public TEntity Add(TEntity entity)
  {
    DoInitializeEntity(entity);
    return _set.Add(entity);
  }
  public void AddOrUpdate(TEntity entity)
  {
    DoInitializeEntity(entity);
    _set.AddOrUpdate(entity);
  }
  public TEntity Attach(TEntity entity)
  {
    DoInitializeEntity(entity);
    return _set.Attach(entity);
  }

  public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, TEntity
  {
    var entity = _set.Create<TDerivedEntity>();
    DoInitializeEntity(entity);
    return 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;


    return entity;
  }

  public TEntity Remove(TEntity entity)
  {
    if (!_set.Local.Contains(entity))
    {
      _set.Attach(entity);
    }
    return _set.Remove(entity);
  }


  public ObservableCollection<TEntity> Local
  {
    get { return _set.Local; }
  }

  IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
  {
    return _set.Where(_filter).GetEnumerator();
  }

  IEnumerator IEnumerable.GetEnumerator()
  {
    return _set.Where(_filter).GetEnumerator();
  }

  Type IQueryable.ElementType
  {
    get { return typeof(TEntity); }
  }

  Expression IQueryable.Expression
  {
    get
    {
      return _set.Where(_filter).Expression;
    }
  }

  IQueryProvider IQueryable.Provider
  {
    get
    {
      return _set.AsQueryable().Provider;
    }
  }

  bool IListSource.ContainsListCollection
  {
    get { return false; }
  }

  IList IListSource.GetList()
  {
    throw new InvalidOperationException();
  }

  void DoInitializeEntity(TEntity entity)
  {
    if (_initializeEntity != null)
      _initializeEntity(entity);
  }

  public DbSqlQuery<TEntity> SqlQuery(string sql, params object[] parameters)
  {
    return _set.SqlQuery(sql, parameters);
  }
}

以上是关于我可以在选择时让我的实体框架DbSet调用我的表值函数吗?的主要内容,如果未能解决你的问题,请参考以下文章

使用实体框架的表值函数

实体框架 4.1 DbSet 重新加载

实体框架,查询包含上下文更改的dbset,而不调用保存更改

如何在实体框架代码优先方法中使用表值函数?

实体框架和DbSet

Entity Framework 4 表值函数