我有一个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是一个字符串。如果是整数,则不起作用!
这对我来说真是太糟糕了。我想到了一些解决方案,但它们并不实用:
我以为我有这个问题舔了“检查引用实体中的字段是否为空”技巧但它在我不完全理解的条件下中断(如果我在引用中没有任何字符串怎么办? 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中获得的关系字段的便利性。有点打败了目的,并添加了更多的代码来维护。
答案 0 :(得分:1)
当您说您的第一个代码段创建OUTER JOIN时就是这种情况,因为B
是实体A
的可选导航属性。对于所需的导航属性,EF将创建一个内部联接(在此处详细说明:https://stackoverflow.com/a/7640489/270591)。
因此,我看到你的上一个代码片段(在LINQ中使用显式join
)的唯一选择 - 除了使用直接SQL之外 - 是要使你的导航属性。
在我看来,这仍然是一个非常丑陋的黑客行为,在其他情况下可能会有意想不到的行为。如果需要导航属性或者可选EF为这种关系添加了“语义”,那就是:如果有外键!= NULL,那么必须是一个相关实体,EF希望你不要t已经删除了数据库中FK约束的强制执行。