【发布时间】:2012-04-27 01:50:01
【问题描述】:
我目前正在尝试将实体框架应用程序与大约十年左右的旧数据库集成。该数据库存在的众多问题之一(除了没有任何关系或约束之外)是几乎每一列都设置为空,即使在几乎所有情况下,这都没有意义。
我总是会遇到这样的异常:
“MyRecord”上的“SortOrder”属性无法设置为“null”值。您必须将此属性设置为“Int32”类型的非空值。
我看到很多关于上述异常的问题,但这些似乎都是真正的错误,因为开发人员没有编写正确表示数据库中数据的类。我想特意写一个不能正确表示数据库中数据的类。我完全知道这违反了 Entity Framework 的规则,这很可能是我在做这件事时遇到这么多困难的原因。
此时无法更改架构,因为它会破坏现有应用程序。也无法修复数据,因为旧应用程序将插入新数据。我想用实体框架来映射数据库,在接下来的几年左右慢慢移动所有应用程序以依赖它进行数据访问,然后最终能够进入数据库重新设计阶段。
我用来解决这个问题的一种方法是透明地代理变量:
internal int? SortOrderInternal { get; set; }
public int SortOrder
{
get { return this.SortOrderInternal ?? 0; }
set { this.SortOrderInternal = value; }
}
然后我可以在 CodeFirst 中映射该字段:
entity.Ignore(model => model.SortOrder);
entity.Property(model => model.SortOrderInternal).HasColumnName("SortOrder");
在此方法中使用 internal 关键字确实可以让我很好地封装这种讨厌的东西,这样我至少可以防止它泄漏到我的数据访问程序集之外。
但不幸的是,我现在无法在查询中使用代理字段,因为将抛出 NotSupportedException:
LINQ to Entities 不支持指定的类型成员“SortOrder”。仅支持初始化程序、实体成员和实体导航属性。
也许一旦 DbSet 接收到表达式,就可以透明地重写表达式?我很想知道这是否可行;我对表达树不够熟练。到目前为止,我未能在 DbSet 中找到一个可以重写以操纵表达式的方法,但我并没有放弃创建一个实现 IDbSet 并传递给 DbSet 的新类,尽管那样会很糟糕。
在调查堆栈跟踪时,我发现了对称为 Shaper 的内部实体框架概念的引用,它似乎是获取数据并将其输入的东西,但使用 dotPeek 调查 System.Data.Entity.dll 表明这肯定会对我有所帮助……假设 Shaper<T> 不是内部和密封的。我几乎可以肯定在这里吠错了树,但我很想知道是否有人以前遇到过这种情况。
【问题讨论】:
-
您是否有不想使用可为空属性的原因?感觉有点像您在尝试“对抗”数据库架构,而不是接受其限制并相应地编写代码。
-
出于兴趣,您为什么不在 CSDL / 模型属性中将实体属性设置为 null=false。虽然底层 SSDL 是可以为空的,它会正确映射非空值,但是如果在 DB 中发现了一个空值,则需要处理一个 ConstraintException(yyy 上的 XXX 属性无法设置为空属性)跨度>
-
@AndrewStephens 我不想这样做,因为这不是数据库的设计方式。例如,还有一种情况,两个不同的实体存储在同一个表中,我使用 Entity Framework 解决了这种情况。我想重新解释应用层中的数据,然后在可能的情况下修复数据库。如果我在实体框架层的类中允许为空值(实际上几乎是每个字段,甚至是外键),我并没有真正解决这个问题。
-
@nonnb - 也许我误解了你,但是一旦我收到 ConstraintException,我就无法访问我请求的数据,除非它隐藏在某个地方。在这个数据库中遇到空值是不可避免的,我希望能够优雅地处理这个问题。如果我可以忽略 ConstraintException 并获取我的数据,我很想听听如何。
-
@Steve - apols,我已将您的 OP 解释为实际上不应该有空值,这将是一个引人注目的事件,而不是常见的事件。
标签: entity-framework expression-trees entity-framework-ctp5