【发布时间】:2012-01-31 19:26:35
【问题描述】:
我正在使用 Entity Framework 4.0(因此我可以将它与 .NET 3.5 一起使用)并且我从现有数据库生成了一个 DAL。
在数据库中,我有两个包含以下列的表(不允许为 NULL):
- tblWeapon(WeaponId PK,WeaponLabel)
- tblShot(ShotId PK,WeaponId)
在 tblShot 的 WeaponId 上有一个外键到 tblWeapon。
然后生成的实体看起来像这样:
public class Weapon {
public int WeaponId { ... }
public string WeaponLabel { ...}
public EntityCollection Shots { ... }
}
public class Shot {
public int ShotId { ... }
public EntityReference WeaponReference { ... }
public Weapon Weapon { ... }
}
在我的代码中,我有一个 ShotFilter 和一个 WeaponFilter 类,它们包含过滤各个表的条件。由于实体的过滤器是动态生成的,我想将查询的生成扩展到相应的过滤器类。每个过滤器都会返回一个IQueryable<T>,它们会根据需要加入以达到预期的结果。
我想要做的是获取所有引用武器的 Shot 对象,其中标签包含文本 0.5。
当尝试对IQueryable<Shot> 与IQueryable<Weapon> 进行内部连接时,问题就出现了,因为Shot 不包含WeaponId 字段(只是一个WeaponReference)。在网上搜索并没有找到太多东西后,我找到了forum post,答案就是加入对象本身。所以我尝试了这个并得到了我期望的结果:
var oWeaponQuery = from w in oDc.Weapons select w;
oWeaponQuery = oWeaponQuery.Where(w => w.Label.Contains("0.5"));
var oShotQuery = from s in oDc.Shots select s;
oShotQuery = oShotQuery.Join(oWeaponQuery, s => s.Weapon, w => w, (s, w) => s);
但是当我检查使用 SQL Server Profiler 查询的实际 SQL 时,我看到了这个糟糕的语句(并且有点呕吐):
SELECT
1 AS [C1],
[Extent1].[ShotId] AS [ShotId],
[Extent1].[WeaponId] AS [WeaponId]
FROM [dbo].[tblShot] AS [Extent1]
INNER JOIN [dbo].[tblWeapon] AS [Extent2] ON EXISTS (SELECT
cast(1 as bit) AS [C1]
FROM ( SELECT cast(1 as bit) AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN (SELECT
[Extent3].[WeaponId] AS [WeaponId]
FROM [dbo].[tblWeapon] AS [Extent3]
WHERE [Extent1].[WeaponId] = [Extent3].[WeaponId] ) AS [Project1] ON 1 = 1
LEFT OUTER JOIN (SELECT
[Extent4].[WeaponId] AS [WeaponId]
FROM [dbo].[tblWeapon] AS [Extent4]
WHERE [Extent1].[WeaponId] = [Extent4].[WeaponId] ) AS [Project2] ON 1 = 1
WHERE ([Project1].[WeaponId] = [Extent2].[WeaponId]) OR (([Project2].[WeaponId] IS NULL) AND ([Extent2].[WeaponId] IS NULL))
)
WHERE (CAST(CHARINDEX(N'0.5', [Extent2].[Label]) AS int)) > 0
那么,我将如何以正确或至少有效的方式做到这一点?或者关于如何以动态和分布式方式组织我的查询生成的任何其他建议?
谢谢!
更新更多细节
我在进行连接时遇到的部分问题是 EF,在为 Shot 生成的实体中,没有 WeaponId 属性。只有 WeaponReference 管理它的属性。所以在我的加入中,我希望能够使用:
oShotQuery = oShotQuery.Join(oWeaponQuery, s => s.WeaponId, w => w.WeaponId, (s, w) => s);
但这不起作用,因为 WeaponId 不是 Shot 的属性。
然后我尝试了这个(这再次看起来很不稳定):
oShotQuery = oShotQuery.Join(oWeaponQuery, s => s.Weapon.WeaponId, w => w.WeaponId, (s, w) => s);
这确实有效,并且产生了相当简洁的 SQL(有一个例外):
SELECT
1 AS [C1],
[Extent1].[ShotId] AS [ShotId],
[Extent1].[WeaponId] AS [WeaponId]
FROM [dbo].[tblShot] AS [Extent1]
INNER JOIN [dbo].[tblWeapon] AS [Extent2] ON ([Extent1].[WeaponId] = [Extent2].[WeaponId]) OR
(([Extent1].[WeaponId] IS NULL) AND ([Extent2].[WeaponId] IS NULL))
WHERE (CAST(CHARINDEX(N'0.5', [Extent2].[Label]) AS int)) > 0
这个例外是:OR (([Extent1].[WeaponId] IS NULL) AND ([Extent2].[WeaponId] IS NULL))。我不想要他们都在NULL,我只想要他们平等的地方。
【问题讨论】:
-
EF4 不能与 .Net 3.5 一起使用,它具有旧版本的 EF AFAIK。但是,无论如何,EF 因生成这样可怕的查询而臭名昭著,除了使用 SPROC 之外,您几乎无能为力。
-
designer.cs文件充满了这样的属性:[global::System.CodeDom.Compiler.GeneratedCode("System.Data.Entity.Design.EntityClassGenerator", "4.0.0.0")]所以我认为它必须是 4.0,我知道它不能运行 EF 4.1 - 这确实需要 .NET 4.0。跨度> -
我很确定它仍然是“EF 3.5”(可能有更新),它可能与 EF4 密切相关,但 EF4 需要 .Net 4 AFAIK。
-
EF 的代码生成比旧的 Linq-to-SQL 差得多。此外,它对 LINQ 功能的支持要弱得多。真可惜。
标签: .net entity-framework-4 .net-3.5 linq-to-entities entityreference