【问题标题】:Is it good practice to cast an entity type inside EF Core Linq Statement在 EF Core Linq 语句中强制转换实体类型是一种好习惯吗
【发布时间】:2021-02-25 16:06:00
【问题描述】:

我在带有 Sqlite 的 Xamarin Forms 4.8 应用程序中使用 Entity Framework Core 3.1。

对于实体,我有一个小的继承树,并使用一个接口来标记某种类型的实体。结构如下所述。在我的数据访问中,我有一些方法只允许用于实现接口INotificationEntity 的实体。数据访问使用具有Entity 类型约束的泛型,因此我在运行时检查实体是否正在实现INotificationEntityEntity 本身从未实现 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 会抛出异常。但是还有其他可能的陷阱或影响吗?

我为什么要这样做?我只是想避免为数据访问、实体、接口和所有内容创建更大的继承树。

【问题讨论】:

  • 在这些方法的约束中添加INotificationEntity不是更好吗?保存自己的演员表。
  • @insane_developer 泛型声明及其约束在类级别,而不是在方法级别。实现类然后声明它们用来覆盖数据访问基类的实际类。
  • 请注意,如果查询完全在服务器上执行,则强制转换可能是无操作的。即,在您的 C# 代码中需要它来编译它,但它可能不会生成任何额外的 SQL 代码。 (分析您的查询以查看它的作用。)
  • 您的DataAccess 类允许超过INotificationEntitys,因此转换可能会失败。
  • @GertArnold 我知道这一点,并在此之前检查该类型是否实现了接口。

标签: c# entity-framework-core entity-framework-core-3.1


【解决方案1】:

请注意,EF-Core 会将 lambda 表达式转换为 SQL。 SQL 对接口一无所知。它知道需要访问特定表上名为 NotificationState 的列。您的强制转换不会影响此列返回的值(就像将数字转换为字符串一样)。

因此,要么存在列,在这种情况下查询成功,要么将失败并出现“列名无效”错误。但是这个演员表不会被翻译成 SQL。这是一个无操作。 lambda 表达式(以及转换)永远不会在客户端执行。

使用Logging in Entity Framework Core 查看生成的 SQL 或使用分析器(如 SQL Server Profiler)。

更新(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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-05
    • 2013-03-28
    • 1970-01-01
    • 2015-05-02
    • 1970-01-01
    • 1970-01-01
    • 2011-10-31
    相关资源
    最近更新 更多