使用 DbContext Set<T>() 而不是在上下文中公开
Posted
技术标签:
【中文标题】使用 DbContext Set<T>() 而不是在上下文中公开【英文标题】:Using DbContext Set<T>() instead of exposing on the context 【发布时间】:2012-11-22 13:11:51 【问题描述】:执行以下操作有什么不同:
public class UsersContext : DbContext
public DbSet<User> Users get; set;
相对于使用上下文的Set<T>
方法:
public class UsersContext : DbContext
var db = new UsersContext();
var users = db.Set<User>();
这些有效地做同样的事情,给我一组用户,但除了你没有通过属性公开该组之外,还有什么大的不同吗?
【问题讨论】:
@abatishchev msdn.microsoft.com/en-us/library/gg696521(v=vs.103).aspx 没有 Set为方便起见添加了Users
属性,因此您无需记住所有表是什么以及对应的类是什么,您可以使用Intellisense查看上下文设计的所有表互动。最终结果在功能上等同于使用Set<T>
。
【讨论】:
用户可能只是一个实例,而在上下文中,我想知道 Set 方法,也返回单个实例? 你想要哪一个?您可以使用 System.Linq.Dynamic。 db.Set使用 Code-First 迁移时,前一种方法会为您带来好处,因为会自动检测到新实体。否则,我很确定它们是等价的。
【讨论】:
我没想到迁移!【参考方案3】:这就是我设置通用 dbSet 的方式,效果很好
DbContext context = new MyContext();
DbSet<T> dbSet = context.Set<T>();
它是更明确的东西的通用版本,例如
DbContext context = new MyContext();
DbSet<User> dbSet = context.Set<User>();
无论哪种方式,它们都是相同的(当T
是User
时)
【讨论】:
错误...好吗?我明白它是如何工作的。我想知道有什么区别/限制。 @Dismissile - 没有限制或差异,除了您可以通过使用泛型定义并传入类型来节省一些重复代码。 Set 应该始终返回单个实例以说明没有限制或差异,对吧? 查看反编译的源代码,DbSet 将是每个 DbContext 实例的同一个实例(例如,如果您多次调用 context.Set一个区别是 Set 方法接受任何类型,包括非实体,它不会抛出异常,只返回该类型的空集。所以如果你输入了错误的类名,你可能会搞砸。
【讨论】:
【参考方案5】:我认为有一些不同。 让我使用问题中的示例。 假设我想做一个基于 User.FirstName 和 User.LastName 的 Any(用户表有更多字段)
方法一:UsersContext.Users.Any(u => u.FirstName.ToLower() == userObj.FirstName && u.LastName.ToLower() == userObj.LastName);
方法二:(UsersContext.Set(typeof(User)) as IQueryable<User>).Any(u => u.FirstName.ToLower() == userObj.FirstName && u.LastName.ToLower() == userObj.LastName);
我在 sql profiler 中检查了 Method1 中触发的查询是:
exec sp_executesql N'SELECT
CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[User] AS [Extent1]
WHERE (((LOWER([Extent1].[FirstName])) = (LOWER(@p__linq__0))) AND ((LOWER([Extent1].[LastName])) = @p__linq__1)
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[User] AS [Extent2]
WHERE (((LOWER([Extent2].[FirstName])) = (LOWER(@p__linq__0))) AND ([Extent2].[LastName] = @p__linq__1)
)) THEN cast(0 as bit) END AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]',@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)',@p__linq__0=N'jack',@p__linq__1=N'saw'
来自方法2:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName],
[Extent1].[Email] AS [Email],
.......other fields......
FROM [dbo].[Users] AS [Extent1]
该表有 40000 条记录,方法 1 大约需要 20 毫秒,而方法 2 大约需要 3500 毫秒。
【讨论】:
Set 可以采用泛型参数。尝试 SetValidationMethod<User>(u => u.FirstName.ToLower() == userObj.FirstName && u.LastName.ToLower() == userObj.LastName)
这样的表达式,这个实习生会做 Set<User>().Any( .. expression passed as param.. )
不确定确切的原因。仍在寻找它。会更新。我认为两种方法之间没有这样的区别,除了 Set<User>()
更适合实现像 Repository
模式这样的数据访问模式,因为 Set<T>()
方法的通用性。
【讨论】:
是的,我知道他们在哪里使用通用存储库,但我很好奇它是否有任何缺点。以上是关于使用 DbContext Set<T>() 而不是在上下文中公开的主要内容,如果未能解决你的问题,请参考以下文章
如何在 EF 中的 DbContext 上使用 Find 或 Find<T>?