我正在使用 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 对象,其中标签包含 text 0.5
。
当尝试对 to 进行内部连接时,问题就出现了IQueryable<Shot>
,IQueryable<Weapon>
因为Shot
它不包含WeaponId
字段(只是 a WeaponReference
)。在网上搜索并没有找到太多东西之后,我找到了一个论坛帖子,答案是加入对象本身。所以我尝试了这个并得到了我期望的结果:
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
,我只想要他们平等的地方。