EF4:过滤掉不存在的引用实体

时间:2011-12-07 20:02:52

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

我有一个Entity Framework 4设计,允许删除引用的表(没有级联删除),而不修改指向它们的实体。因此,例如,实体A在ID字段中具有对实体B的外键引用。可以删除B(并且数据库中没有FK约束来阻止它),所以如果我查看ABID它总是一个有效字段(因为所有这一切都返回A中的ID字段)即使没有由于先前的删除而记录了具有该ID的B.这是设计的,我不希望级联删除,我需要A记录坚持一段时间用于审计目的。

问题是过滤掉不存在的已删除记录并不像听起来那么容易。例如,如果我这样做:

from c in A
select A.B.somefield;

这导致生成的SQL中的OUTER JOIN,因此即使它们引用缺少的B记录,它也会获取所有A记录。所以,我一直用来解决这个问题(因为我无法找到更好的方法!)是添加一个where子句来检查引用的B记录中的字符串字段。如果B实体中的那个字段为空,那么我假设B不存在。

from c in A
where c.B.somestringfield != null
select A.B.somefield;

似乎工作如果B.somestringfield是一个字符串。如果是整数,则不起作用!

这对我来说真是太糟糕了。我想到了一些解决方案,但它们并不实用:

  1. 查询删除B时引用B的所有表,并将其外键清空。这太难看了,如果我将来添加另一个引用B的实体,我不想记住这样做。更不用说每当我删除某些内容时都会解决所有引用的巨大性能延迟。
  2. 将字符串字段添加到我可以依赖的每个表中,我可以检查该实体是否存在。 Blech,我不想为此添加数据库字段。
  3. 实现软删除并保持所有参考完整性 - 基本上设置级联删除,但这将导致巨大的数据库膨胀,因为我无法清理由于引用而导致的大量记录。不行。
  4. 我以为我有这个问题舔了“检查引用实体中的字段是否为空”技巧但它在我不完全理解的条件下中断(如果我在引用中没有任何字符串怎么办? table?什么类型的字段可以工作?整数不会。)

    作为一个例子,如果我在实体B中有一个整数字段“count”,我检查它是否为null,如:

    from c in A
    where c.B.count != null
    select c.B.count;
    

    我得到一堆带有null的记录,其中包含与结果混合的计数,实​​际上查询会弹出“InvalidOperationException:强制转换为值类型'Int32'失败,因为具体化值为null。结果是type的泛型参数或查询必须使用可空类型。“

    所以我需要做

    from c in A
    where c.B.count != null
    select new { count = (int?)c.B.count };
    

    甚至可以看到空记录。所以这对我来说非常困惑,这个查询如何在结果中产生空记录。

    我刚刚发现了一些东西,如果我像这样进行显式连接,那么SQL就是INNER JOIN,一切都很好:

    from c in A
    join j in B on A.B.ID equals j.ID
    select c;
    

    但这很糟糕。我将不得不修改大量的查询以添加显式连接子句,而不是享受我在EF中获得的关系字段的便利性。有点打败了目的,并添加了更多的代码来维护。

1 个答案:

答案 0 :(得分:1)

当您说您的第一个代码段创建OUTER JOIN时就是这种情况,因为B是实体A的可选导航属性。对于所需的导航属性,EF将创建一个内部联接(在此处详细说明:https://stackoverflow.com/a/7640489/270591)。

因此,我看到你的上一个代码片段(在LINQ中使用显式join)的唯一选择 - 除了使用直接SQL之外 - 是要使你的导航属性。

在我看来,这仍然是一个非常丑陋的黑客行为,在其他情况下可能会有意想不到的行为。如果需要导航属性或者可选EF为这种关系添加了“语义”,那就是:如果有外键!= NULL,那么必须是一个相关实体,EF希望你不要t已经删除了数据库中FK约束的强制执行。