首页 > 解决方案 > 更改 DbSet 类型参数时,EF Core 的行为有所不同

问题描述

我正在使用 EF Core 5.0.5

我有一个数据库,其中包含两个表“t_ErrorLogs_Ciliate_IN”和“t_ErrorLogs_Ciliate_OUT”,它们具有相同的列。一个进程写入其中一个表,另一个进程写入另一个,但记录的结构完全相同,这就是它们具有相同列的原因。

我的 DbContext 看起来像这样:

public class CiliateLoggingContext : DbContext
{
    public CiliateLoggingContext() { }

    public CiliateLoggingContext(DbContextOptions options) : base(options) { }

    public virtual DbSet<ErrorLog_Ciliate_IN> t_ErrorLogs_Ciliate_IN { get; set; }

    public virtual DbSet<ErrorLog_Ciliate_OUT> t_ErrorLogs_Ciliate_OUT { get; set; }
}

“ErrorLog_Ciliate_IN”和“ErrorLog_Ciliate_OUT”类只是:

public partial class ErrorLog_Ciliate_IN : CiliateErrorLogBase { }

public partial class ErrorLog_Ciliate_OUT : CiliateErrorLogBase { }

public class CiliateErrorLogBase
{
    // properties representing the columns.
}

如果我尝试选择这样的日志:

Task<List<ErrorLog_Ciliate_IN>> SelectMethod()
{
    context.t_ErrorLogs_Ciliate_IN.Where(log => log.Id > 10).OrderByDescending(log => log.Id).Take(20).ToListAsync();
}

一切都很好,我得到了一个任务,我等待它,它给了我一个ErrorLogs_Ciliate_IN对象列表。

极好的!

但是,由于“ErrorLog_Ciliate_IN”和“ErrorLog_Ciliate_OUT”类完全相同,我想创建一个“ErrorLog_Ciliate”类并使用它。

public partial class ErrorLog_Ciliate : CiliateErrorLogBase { }

并将 DbContext 更改为:

public class CiliateLoggingContext : DbContext
{
    public CiliateLoggingContext() { }

    public CiliateLoggingContext(DbContextOptions options) : base(options) { }

    public virtual DbSet<ErrorLog_Ciliate> t_ErrorLogs_Ciliate_IN { get; set; }

    public virtual DbSet<ErrorLog_Ciliate> t_ErrorLogs_Ciliate_OUT { get; set; }
}

然而 EF 突然决定从“ErrorLog_Ciliate”中选择,而不是“t_ErrorLogs_Ciliate_IN”。

这就是“ToQueryString()”在 DbContext 的第一个版本中返回的内容 - 每个表都有一个单独的类:

方法:

string SelectIN()
{
    context.t_ErrorLogs_Ciliate_IN.Where(log => log.Id > 10).OrderByDescending(log => log.Id).Take(20).ToListAsync();
}


string SelectOUT()
{
    context.t_ErrorLogs_Ciliate_OUT.Where(log => log.Id > 10).OrderByDescending(log => log.Id).Take(20).ToListAsync();
}

结果:

DECLARE @__p_0 int = 20;

SELECT TOP(@__p_0) [t].[Id], [t].[Date], [t].[Type]
FROM [t_ErrorLogs_Ciliate_IN] AS [t]
WHERE [t].[Id] > 10
ORDER BY [t].[Id] DESC



DECLARE @__p_0 int = 20;

SELECT TOP(@__p_0) [t].[Id], [t].[Date], [t].[Type]
FROM [t_ErrorLogs_Ciliate_OUT] AS [t]
WHERE [t].[Id] > 10
ORDER BY [t].[Id] DESC

如果我使用 DbContext 的第二个版本,其中两个 DbSet 使用相同的类型参数,ErrorLog_Ciliate最奇怪的事情会发生:

DECLARE @__p_0 int = 20;

SELECT TOP(@__p_0) [a].[Id], [a].[Data], [a].[Type]
FROM [CiliateErrorLog] AS [a]
WHERE [a].[Id] > 10
ORDER BY [a].[Id] DESC


DECLARE @__p_0 int = 20;

SELECT TOP(@__p_0) [a].[Id], [a].[Data], [a].[Type]
FROM [CiliateErrorLog] AS [a]
WHERE [a].[Id] > 10
ORDER BY [a].[Id] DESC

注意它现在是如何尝试从类的名称中选择的,而之前它是如何尝试从 DbContext 参数的名称中选择的。

如果我使用 DbContext 的混合版本,它具有:

    public virtual DbSet<ErrorLog_Ciliate> t_ErrorLogs_Ciliate_IN { get; set; }

    public virtual DbSet<ErrorLog_Ciliate_OUT> t_ErrorLogs_Ciliate_OUT { get; set; }

我得到了正常的第一个结果:

DECLARE @__p_0 int = 20;

SELECT TOP(@__p_0) [t].[Id], [t].[Date], [t].[Type]
FROM [t_ErrorLogs_Ciliate_IN] AS [t]
WHERE [t].[Id] > 10
ORDER BY [t].[Id] DESC

DECLARE @__p_0 int = 20;

SELECT TOP(@__p_0) [t].[Id], [t].[Date], [t].[Type]
FROM [t_ErrorLogs_Ciliate_OUT] AS [t]
WHERE [t].[Id] > 10
ORDER BY [t].[Id] DESC

为什么会发生这种情况,我如何对两个属性使用相同的类型参数并且仍然让 EF 识别它应该从属性名称中选择“来自”?

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

解决方案


EF 仅支持每个实体类型在模型中的一个映射 per DbContext,因此您不能将相同类型的两个实例映射到同一上下文中的不同表。如果您查看EF Fluent API,您会发现它基于类型而不是DbSet's. 因此,要在不同的表之间共享表结构,就像您自己已经完成的那样,您需要使用继承 - 最常见的方法是从相同的基类或更深奥的继承实体类型 -在 .inheritDbContext中的覆盖实体设置OnModelCreating


推荐阅读