在 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; }
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();
}
}
这段代码完美运行,但是生成的查询非常奇怪和复杂,特别是有很多 CASE WHEN
SELECT
[Limit1].[C1] AS [C1],
[Limit1].[Id] AS [Id],
[Limit1].[C2] AS [C2],
[Limit1].[C3] AS [C3],
[Limit1].[C4] AS [C4],
[Limit1].[C5] AS [C5]
FROM ( SELECT TOP (1)
[Extent1].[Id] AS [Id],
CASE WHEN ([Extent1].[Discriminator] = N'BaseType') THEN '0X' WHEN ([Extent1].[Discriminator] = N'TypeA') THEN '0X0X' WHEN ([Extent1].[Discriminator] = N'TypeB') THEN '0X1X' ELSE '0X1X0X' END AS [C1],
CASE WHEN ([Extent1].[Discriminator] = N'BaseType') THEN CAST(NULL AS varchar(1)) WHEN ([Extent1].[Discriminator] = N'TypeA') THEN [Extent1].[PropA] WHEN ([Extent1].[Discriminator] = N'TypeB') THEN CAST(NULL AS varchar(1)) END AS [C2],
CASE WHEN ([Extent1].[Discriminator] = N'BaseType') THEN CAST(NULL AS decimal(18,2)) WHEN ([Extent1].[Discriminator] = N'TypeA') THEN CAST(NULL AS decimal(18,2)) WHEN ([Extent1].[Discriminator] = N'TypeB') THEN [Extent1].[PropB] ELSE [Extent1].[PropB] END AS [C3],
CASE WHEN ([Extent1].[Discriminator] = N'BaseType') THEN CAST(NULL AS int) WHEN ([Extent1].[Discriminator] = N'TypeA') THEN CAST(NULL AS int) WHEN ([Extent1].[Discriminator] = N'TypeB') THEN [Extent1].[PropEnum] ELSE [Extent1].[PropEnum] END AS [C4],
CASE WHEN ([Extent1].[Discriminator] = N'BaseType') THEN CAST(NULL AS int) WHEN ([Extent1].[Discriminator] = N'TypeA') THEN CAST(NULL AS int) WHEN ([Extent1].[Discriminator] = N'TypeB') THEN CAST(NULL AS int) ELSE [Extent1].[PropC] END AS [C5]
FROM [dbo].[BaseTypes] AS [Extent1]
WHERE ([Extent1].[Discriminator] IN (N'TypeA',N'TypeB',N'TypeC',N'BaseType')) AND (1 = [Extent1].[Id])
) AS [Limit1]
除了多次无用的成本THEN CAST(NULL as X),我的项目中的查询很大(> 50 KB),因为我有很多派生类,包含很多特性。如您所料,我的 DBA 团队不高兴看到对我们的数据库进行这种查询。
如果我删除 TypeB 上的枚举属性,请求会更清晰。如果我只有两个层次结构级别,也就是 class TypeC : BaseType(与示例中的 3 相比,因为 class TypeC : 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
一个常见的解决方案是为整数值创建一个单独的属性并忽略枚举属性。这行得通,但为了同一目的而拥有 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 上发现了一个错误:https://entityframework.codeplex.com/workitem/2117 .好像没有解决。
最佳答案
关于使用 EF/Large 查询
我已经完成了一些 EF6 和半大型层次结构的工作。有几件事你应该考虑。首先,为什么您的 DBA 团队对这些查询不满意。当然,这些不是他们会编写的查询,但假设管理层不希望您花时间从头开始编写每个查询,他们将不得不接受您使用 ORM 框架并且该 ORM 框架可能导致查询有点大。
现在,如果他们有特定的性能问题,您应该解决这些问题。
你能做什么
现在您可以做什么来清理您的查询。
1) 使所有可以抽象的类都抽象。
2) 密封所有其他类。
3) 在您的 linq 查询中,尽可能转换为具体类型(使用 OfType() )。这甚至可能比 .Select(x => x as SomethingHere) 更有效。如果您有一个特别令人讨厌的查询,可能需要进行一些实验才能最好地从 linq 调整您的查询。
解释我通过实验发现的内容
正如您在查询中注意到的那样,它正在检查鉴别器。如果您的查询变得有点复杂(我希望那些 50k 查询是其中之一),您会看到它添加了用于字符串连接的代码以检查每个可能的组合。你看到在
THEN '0X' WHEN ([Extent1].[Discriminator] = N'TypeA') THEN '0X0X'
部分。 我做了一些 POC 试图找出这种行为,似乎正在发生的是 Entity Framework 正在将属性转换为“方面”(我的术语)。例如,如果翻译后的字符串包含“0X”或“0X0X”,则类将具有“PropertyA”。 PropertyB 可能会翻译成“R2D2”,PropertyC 可能会翻译成“C3P0”。这样,如果一个类名被翻译成“R2D2C3P0”。它知道它同时拥有 PropertyB 和 PropertyC。它必须考虑一些隐藏的派生类型和所有父类(super class)型。现在,如果 Entity Framework 可以更加确定您的类层次结构(通过密封类),它可以简化这里的逻辑。根据我的经验,EF 生成的字符串构建逻辑可能比您在此处显示的逻辑更复杂。这就是为什么使类抽象/密封 EF 可以更聪明地解决这个问题并减少您的查询。
另一个性能提示
现在还要确保鉴别器列上有正确的索引。 (您可以从 Entity Framework 内的 DbMigration 脚本执行此操作)。
“绝望”绩效指标
现在,如果所有其他方法都失败了,请将您的鉴别器设置为 int。这会损害数据库/查询的可读性很多,但它有助于提高性能。 (你甚至可以让你所有的类自动发出一个包含类名的属性,这样你就可以在你的数据库中保持一些类型的可读性)。
更新:
在 RX_DID_RX 的评论之后进行更多研究后发现,如果不使用动态代理生成,则只能密封/制作 poco 的摘要。 (延迟加载和更改跟踪)。在我的特定应用中,我们没有使用它,所以它对我们来说效果很好,但我必须撤消我之前的建议。
有关 EF6 特定链接的更多详细信息 http://www.entityframeworktutorial.net/Types-of-Entities.aspx
不过,添加索引并在 linq 查询中使用转换仍然有帮助。
关于c# - 带有 TPH 和枚举的 Entity Framework 中的多个 CASE WHEN,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33900031/
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只