实体框架 - 外键的内在联系

时间:2012-01-31 19:26:36

标签: .net entity-framework-4 .net-3.5 linq-to-entities entityreference

我正在使用Entity Framework 4.0(所以我可以在.NET 3.5中使用它)并且我从现有数据库生成了DAL。

在数据库中,我有两个包含以下列的表(Nothing不允许为NULL):

  1. tblWeapon(WeaponId PK,WeaponLabel)
  2. tblShot(ShotId PK,WeaponId)
  3. 在tblShot的WeaponId to 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的地方,我只想要他们平等的地方。

1 个答案:

答案 0 :(得分:0)

  

我想要做的是获取引用武器的所有Shot对象,其中标签包含文本0.5。

这是我使用的查询:

var oShotQuery = oDc.Shots.Where(s => s.Weapon.Label.Contains("0.5"))

就生成的SQL而言,如果它引入了一堆您根本不想使用的东西,请不要推迟。这些查询往往至少与您手动生成的查询一样好。关键是要保持原始查询的简单。

更新

  

我希望能够动态生成不同的查询,然后再将它们连接在一起。

这个怎么样?

var oShotQuery = oShotQuery.Where(
    o => oWeaponQuery.Any(w => w.WeaponId == o.Weapon.WeaponId))