在 EF Core Linq 语句中转换实体类型是一种好习惯吗
Is it good practice to cast an entity type inside EF Core Linq Statement
我在带有 Sqlite 的 Xamarin Forms 4.8 应用程序中使用 Entity Framework Core 3.1。
对于实体,我有一个小的继承树,并使用一个接口来标记某种类型的实体。结构说明如下。在我的数据访问中,我有一些方法只允许实现接口 INotificationEntity
的实体使用。数据访问使用具有 Entity
类型蜂鸣类型约束的泛型,因此我在运行时检查实体是否正在实现 INotificationEntity
。 Entity
本身从未实现 INotificationEntity
.
在我的数据访问中,我使用以下代码,我在 Where 和 CountAsync() 语句中强制转换为接口:
public abstract class DataAccess<TTaskData, TEntity> : DataAccessBase,
IDataAccess<TData>
where TData : Data
where TEntity : Entity, new()
...
public async Task<int> CountAsync()
{
...
// usage in CountAsync
return await dbSet.CountAsync(x => ((INotificationEntity)x).NotificationState != NotificationState.Done);
...
}
public async Task<int> GetPendingAsync(int tvdNumber)
{
....
// usage in Where
var result = await dbSet
.AsNoTracking()
.Where(x => ((INotificationEntity)x).NotificationState != NotificationState.Done)
.OrderByDescending(x => x.EventDate)
.ToListAsync()
.ConfigureAwait(false);
...
}
实体的简化结构:
public interface INotificationEntity
{
NotificationState NotificationState { get; set; }
}
public abstract class Entity
{
public int Id { get; set; }
public DateTime EventDate { get; set; }
}
public class NotificationEntity : Entity, INotificationEntity
{
public NotificationState NotificationState { get; set; }
}
代码正在编译和工作。我不是 100% 知道,如果有任何不想要的事情发生。据我了解文档,如果这会导致客户端评估查询,EF Core 3.1 将抛出异常。但是还有其他可能的陷阱或影响吗?
我为什么要这样做?我只是想避免为数据访问、实体、接口和所有东西使用更大的继承树。
请注意,EF-Core 会将 lambda 表达式转换为 SQL。 SQL 对接口一无所知。它理解需要访问特定 table 上名为 NotificationState
的列。您的转换不会影响此列返回的值(就像将数字转换为字符串一样)。
因此,要么该列存在(在这种情况下查询成功),要么失败并出现“无效的列名”错误。但是这个 cast 不会被翻译成 SQL。这是一个空操作。 lambda 表达式(以及强制转换)永远不会在客户端执行。
使用 Logging in Entity Framework Core 查看生成的 SQL 或使用分析器(如 SQL 服务器分析器)。
更新(OP):
除了上面给出的正确答案之外,我想添加生成的 Sql 语句作为证明。
// Linq sample with CountAsync()
return await dbSet.CountAsync(x => ((INotificationTask)x).NotificationState !=
NotificationState.Done);
// Generated Sql:
SELECT COUNT(*)
FROM "BTable" AS "b"
WHERE ("b"."NotificationState" <> 1)
// Linq Samle with Where / ToList
var result = await dbSet
.AsNoTracking()
.Where(x => ((INotificationTask)x).NotificationState != NotificationState.Done &&
((INotificationTask)x).TvdNumber == tvdNumber)
.OrderByDescending(x => x.EventDate)
.ToListAsync()
.ConfigureAwait(false);
// Generated Sql:
SELECT "l"."Id", "l"."EventDate", "l"."GroupingAttribute", "l"."NotificationState"
FROM "LTable" AS "l"
WHERE ("l"."NotificationState" <> 1)
ORDER BY "l"."EventDate" DESC
我在带有 Sqlite 的 Xamarin Forms 4.8 应用程序中使用 Entity Framework Core 3.1。
对于实体,我有一个小的继承树,并使用一个接口来标记某种类型的实体。结构说明如下。在我的数据访问中,我有一些方法只允许实现接口 INotificationEntity
的实体使用。数据访问使用具有 Entity
类型蜂鸣类型约束的泛型,因此我在运行时检查实体是否正在实现 INotificationEntity
。 Entity
本身从未实现 INotificationEntity
.
在我的数据访问中,我使用以下代码,我在 Where 和 CountAsync() 语句中强制转换为接口:
public abstract class DataAccess<TTaskData, TEntity> : DataAccessBase,
IDataAccess<TData>
where TData : Data
where TEntity : Entity, new()
...
public async Task<int> CountAsync()
{
...
// usage in CountAsync
return await dbSet.CountAsync(x => ((INotificationEntity)x).NotificationState != NotificationState.Done);
...
}
public async Task<int> GetPendingAsync(int tvdNumber)
{
....
// usage in Where
var result = await dbSet
.AsNoTracking()
.Where(x => ((INotificationEntity)x).NotificationState != NotificationState.Done)
.OrderByDescending(x => x.EventDate)
.ToListAsync()
.ConfigureAwait(false);
...
}
实体的简化结构:
public interface INotificationEntity
{
NotificationState NotificationState { get; set; }
}
public abstract class Entity
{
public int Id { get; set; }
public DateTime EventDate { get; set; }
}
public class NotificationEntity : Entity, INotificationEntity
{
public NotificationState NotificationState { get; set; }
}
代码正在编译和工作。我不是 100% 知道,如果有任何不想要的事情发生。据我了解文档,如果这会导致客户端评估查询,EF Core 3.1 将抛出异常。但是还有其他可能的陷阱或影响吗?
我为什么要这样做?我只是想避免为数据访问、实体、接口和所有东西使用更大的继承树。
请注意,EF-Core 会将 lambda 表达式转换为 SQL。 SQL 对接口一无所知。它理解需要访问特定 table 上名为 NotificationState
的列。您的转换不会影响此列返回的值(就像将数字转换为字符串一样)。
因此,要么该列存在(在这种情况下查询成功),要么失败并出现“无效的列名”错误。但是这个 cast 不会被翻译成 SQL。这是一个空操作。 lambda 表达式(以及强制转换)永远不会在客户端执行。
使用 Logging in Entity Framework Core 查看生成的 SQL 或使用分析器(如 SQL 服务器分析器)。
更新(OP):
除了上面给出的正确答案之外,我想添加生成的 Sql 语句作为证明。
// Linq sample with CountAsync()
return await dbSet.CountAsync(x => ((INotificationTask)x).NotificationState !=
NotificationState.Done);
// Generated Sql:
SELECT COUNT(*)
FROM "BTable" AS "b"
WHERE ("b"."NotificationState" <> 1)
// Linq Samle with Where / ToList
var result = await dbSet
.AsNoTracking()
.Where(x => ((INotificationTask)x).NotificationState != NotificationState.Done &&
((INotificationTask)x).TvdNumber == tvdNumber)
.OrderByDescending(x => x.EventDate)
.ToListAsync()
.ConfigureAwait(false);
// Generated Sql:
SELECT "l"."Id", "l"."EventDate", "l"."GroupingAttribute", "l"."NotificationState"
FROM "LTable" AS "l"
WHERE ("l"."NotificationState" <> 1)
ORDER BY "l"."EventDate" DESC