C# 在具有TPH和枚举的实体框架中时出现多个案例
在EF 6.1.3上使用TPH时,我有一个非常奇怪的行为。 下面是一个基本的例子:C# 在具有TPH和枚举的实体框架中时出现多个案例,c#,.net,entity-framework,C#,.net,Entity Framework,在EF 6.1.3上使用TPH时,我有一个非常奇怪的行为。 下面是一个基本的例子: public class BaseType { public int Id { get; set; } } public class TypeA : BaseType { public string PropA { get; set; } } public class TypeB : BaseType { public decimal PropB { get; set; } pub
public class BaseType
{
public int Id { get; set; }
}
public class TypeA : BaseType
{
public string PropA { get; set; }
}
public class TypeB : BaseType
{
public decimal PropB { get; set; }
public OneEnum PropEnum { get; set; }
}
public class TypeC : TypeB
{
public int PropC { get; set; }
}
public enum OneEnum
{
Foo,
Bar
}
public partial class EnumTestContext : DbContext
{
public EnumTestContext()
{
this.Database.Log = s => { Debug.WriteLine(s); };
}
public DbSet<BaseType> BaseTypes { get; set; }
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<EnumTestContext>());
using (var context = new EnumTestContext())
{
context.BaseTypes.Add(new TypeA() { Id = 1, PropA = "propA" });
context.BaseTypes.Add(new TypeB() { Id = 2, PropB = 4.5M, /*PropEnum = OneEnum.Bar*/ });
context.BaseTypes.Add(new TypeC() { Id = 3, PropB = 4.5M, /*PropEnum = OneEnum.Foo,*/ PropC = 123 });
context.SaveChanges();
var onetype = context.BaseTypes.Where(b => b.Id == 1).FirstOrDefault();
Console.WriteLine("typeof {0} with {1}", onetype.GetType().Name, onetype.Id);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
除了多个和无用的THEN CAST(NULL为X)的代价外,在我的项目中查询很大(>50kbs),因为我有很多派生类,包含很多属性。正如您所料,我的DBA团队不喜欢看到这种对数据库的查询
如果我删除TypeB上的枚举属性,请求会更干净。如果我只有两个层次结构级别,即akaclasstypec:BaseType
(与示例中的3相比,因为classtypec:TypeB
)
是否有任何设置、模型配置或解决方法来避免这种奇怪的行为
更新
下面是删除TypeB.PropEnum时生成的查询
SELECT TOP (1)
[Extent1].[Discriminator] AS [Discriminator],
[Extent1].[Id] AS [Id],
[Extent1].[PropA] AS [PropA],
[Extent1].[PropB] AS [PropB],
[Extent1].[PropC] AS [PropC]
FROM [dbo].[BaseTypes] AS [Extent1]
WHERE ([Extent1].[Discriminator] IN (N'TypeA',N'TypeB',N'TypeC',N'BaseType')) AND (1 = [Extent1].[Id])
更新2
一种常见的解决方案是创建一个单独的属性,即整数值,并忽略枚举属性。这是可行的,但如果有两个属性用于相同的目的,那就相当混乱了
public class TypeB : BaseType
{
public decimal PropB { get; set; }
public int PropEnumValue { get; set; }
[NotMapped]
public OneEnum PropEnum
{
get { return (OneEnum)PropEnumValue; }
set { PropEnumValue = (int)value; }
}
}
更新3
我在codeplex上发现了一个bug:。这似乎还没有解决。关于使用EF/大型查询 我对EF6和半大型层次结构做了一些工作。有几件事你应该考虑。首先,为什么您的DBA团队对此类查询不满意。当然,这些查询并不是他们将要编写的查询,但是假设管理层不希望您花时间从头开始编写每个查询,他们将不得不接受这样一个事实,即您使用的是ORM框架,ORM框架可能会导致更大的查询 现在,如果他们有特定的性能问题,您应该解决这些问题 你能做什么 现在,您可以做些什么来清理您的查询 1) 使所有可以是抽象的类都成为抽象的 2) 将所有其他类密封起来 3) 在linq查询中,尽可能将其转换为具体类型(使用OfType()。这甚至可能比.Select(x=>x作为SomethingHere)更有效。如果您有一个特别讨厌的查询,那么可能需要进行一些实验,以确定从linq优化查询的最佳方式 解释我通过实验发现的内容 当你注意到你的查询时,它正在检查鉴别器。如果查询变得更复杂一些(我希望50k查询就是其中之一),您将看到它添加了用于字符串连接的代码,以检查每个可能的组合。你可以看到这种情况在未来发生了一些
THEN '0X' WHEN ([Extent1].[Discriminator] = N'TypeA') THEN '0X0X'
部分。
我已经做了一些POC试图弄清楚这种行为,似乎正在发生的是实体框架正在将属性转换为“方面”(我的术语)。例如,如果翻译的字符串包含“0X”或“0X0X”,则类将具有“PropertyA”。PropertyB它可以翻译为“R2D2”,PropertyC可以翻译为“C3P0”。如果一个类名被翻译成“R2D2C3P0”,那么就是这样。它知道它同时拥有PropertyB和PropertyC。它必须考虑一些隐藏的派生类型和所有的超类型。现在,如果enity framework可以更确定您的类层次结构(通过密封类),那么它可以简化这里的逻辑。根据我的经验,EF生成的字符串构建逻辑可能比这里显示的还要复杂。这就是为什么让类抽象/密封EF可以更聪明地处理这一点,并减少您的查询
另一个性能提示
现在还要确保鉴别器列上有正确的索引。(您可以从实体框架内的DbMigration脚本执行此操作)
“消极怠工”绩效衡量标准
现在,如果所有其他方法都失败,请将您的鉴别器设置为int。这将严重影响数据库/查询的可读性,但有助于提高性能。(您甚至可以让所有类自动发出一个包含类名的属性,以便在数据库中保持某些类型的可读性)
更新:
在RX_Dod_RX的评论之后进行了更多的研究,结果表明,如果不使用动态代理生成,您只能密封/制作poco的摘要。(延迟加载和更改跟踪)。在我的特定应用程序中,我们没有使用它,因此它对我们很有效,但我必须回复我先前的建议
有关更多详细信息,请访问EF6特定链接
添加索引和在linq查询中使用casting仍然有帮助。来自巴达维亚,回答有关查询的问题:“现在,如果他们有特定的性能问题,您应该解决这些问题”,不要浪费时间处理其他查询。另外,不要浪费时间去理解EF生成查询的原因(如果您使用Include跟踪LINQ查询,您将对生成的查询留下负面印象)。
您需要解决的其他查询是与EF提供程序不兼容的查询(如有时EF生成的交叉连接查询) 关于SQL语句中的性能(在DML中,您还可以找到有关stackoverflow的其他几个问题):
-如果需要,可以使用存储过程
-EF中缺少一个功能。不能运行SQL查询并使用EF中定义的映射将其映射到类。您可以在这里找到一个实现(实际上,它可能需要一些修复程序才能使用TPH) 就性能而言,EF不能很好地处理大型类层次结构,即使是在TPH的情况下(至少EF5是这样)。如果您计划创建巨大的类层次结构,我强烈建议您不要使用EF,但是为什么我只在枚举上有这个问题呢?如果我删除了所有枚举,那么即使有大的层次结构,SQL也总是干净的。对不起,我使用的上一个版本(EF 5)不支持枚举。我们无法将枚举存储为int。也许你应该试试类似的东西。尝试将枚举值存储为整数,并查看其影响performance@Cybermaxs你为什么不满足
THEN '0X' WHEN ([Extent1].[Discriminator] = N'TypeA') THEN '0X0X'