我可以让我的 Entity Framework DbSet 在选择时调用我的 table 值函数吗?
Can I make my Entity Framework DbSet call my table valued function when selecting?
我有许多使用 DbSet<dbo_Deal>
的现有查询,现在需要过滤掉未授权用户的机密交易。我想知道是否有一种方法可以覆盖 DbSet<dbo_Deal>
以便它选择使用 Table 值参数而不是其默认行为。
我创建了以下 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 在选择或加入交易时使用此函数,那就太好了。这可能吗?
尝试 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 函数获得按我想要的方式工作的解决方案,因此我改用 FilteredDbSet 来包装 DbSet。实现它所需要做的就是在 DbContext 中的 属性 上创建 return 类型 FilteredDbSet
,然后在构造函数中使用我想要的过滤器实例化它。我还在 public 下面的 class 中创建了私有构造函数,这样我就可以模拟它进行单元测试。
这对我来说是一个非常好的解决方案,因为我避免了重构所有现有的 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<dbo_Deal>
的现有查询,现在需要过滤掉未授权用户的机密交易。我想知道是否有一种方法可以覆盖 DbSet<dbo_Deal>
以便它选择使用 Table 值参数而不是其默认行为。
我创建了以下 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 在选择或加入交易时使用此函数,那就太好了。这可能吗?
尝试 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 函数获得按我想要的方式工作的解决方案,因此我改用 FilteredDbSet 来包装 DbSet。实现它所需要做的就是在 DbContext 中的 属性 上创建 return 类型 FilteredDbSet
,然后在构造函数中使用我想要的过滤器实例化它。我还在 public 下面的 class 中创建了私有构造函数,这样我就可以模拟它进行单元测试。
这对我来说是一个非常好的解决方案,因为我避免了重构所有现有的 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);
}
}