.NET ORM,不可变值对象,结构,默认构造函数和只读属性

时间:2011-03-19 20:12:22

标签: .net orm class-design immutability value-type

我刚刚开始使用.NET ORM,我还没有在Entity Framework和NHibernate之间做出决定。但在这两种情况下,我遇到了一个问题,因为他们似乎希望我以各种方式破坏我的域模型的完整性,特别是在C#对象设计的更精细点上。这是关于这个问题的几个问题之一。


我非常习惯使用如下所示的模式在适当的属性上强制执行不变性:

public class Foo
{
    private readonly string bar;
    public string Bar { return this.bar; }

    public Foo(string bar)
    {
        this.bar = bar;
    }
}

这似乎不受NHibernate或Entity Framework的支持。他们想要默认的构造函数和public setter;它甚至出现private setter(和默认构造函数)(有时是?),因为ORM可以使用反射。

我想我可以使用private setter和private默认构造函数来解决这些问题。至少那时公共API不会受到损害。只是我正在修改我所有类的实现以添加未使用的private构造函数,并且必须信任future-Domenic,他理解我的setter上的private真的意味着“除了我之外不要打电话给我在构造函数中。“持久层泄漏到我的域对象设计中。

这也似乎没有必要---为什么ORM不能知道使用非默认构造函数?也许他们能够,我只是找不到正确的博客文章解释如何。

最后,在某些情况下,我的不可变值对象实际上非常适合作为(不可变的)值类型,即struct s。我的猜测是这是可能的,因为在数据库中,我的struct的字段将显示在父实体存储的同一行中。你能确认/否认吗? This blog post看起来很有希望,因为它给出了肯定的答案,但代码的数量(实际上特定于所讨论的值类型)却让人头脑发昏。


令人沮丧的是,经过几年阅读像 Effective C#这样的书籍或像Eric Lippert那样的博客,它们提供了关于如何设计富有表现力和防弹的C#对象的建议,使用ORM的需求是让我把大部分知识扔出窗外。我希望这里有人可以指出我错在哪里,要么掌握他们的能力,要么是我对域建模和ORM角色的思考。

2 个答案:

答案 0 :(得分:1)

正如评论所指出的那样,当您/采用ORM时,您将不得不做出一些妥协。在我看来,要记住的事情是,生产力的提高远远超过这些妥协的“成本”。

我确实想指出你(因为你是EF的新手),你可以customize the T4 templates生成EF实体和上下文。我想如果你玩这个,你就可以解决大部分你不喜欢的事情。

答案 1 :(得分:0)

使用过许多ORM令我感到痛苦,使用接口,抽象和不可变对象无法在大多数ORM中正常使用。正是出于这个原因,以及大约8年前我写的其他文章,我已经使用自己的ORM发现了其中一些问题,并找到了解决这些问题的方法。我会看一下微型ORM,然后看看您能找到什么,那里有很多东西,并且功能丰富,很多行李更少。

我添加了一些新功能来处理我的一些痛苦。像返回接口或其他抽象类型以及不可变类型的集合一样。

    string sql = "Select * from simpleEntities";
    ISqlQuery query = _factory.CreateSqlQuery(sql, nameof(AbstractFactoryTests.ReadImmutableEntityItems));
    IList<IImmutableEntity> items = loader.ObtainItemsImmutable<IImmutableEntity>(query);

或:

    IList<ISqlQuery> queries = new List<ISqlQuery>();

    // Note, no register types needed here because of returnType parameter

    ISqlQuery s1 = _factory.CreateSqlQuery("Select * from Employee where EmployeeType=1", "LoadAbstract.ParallelLoadItems1", typeof(Employee));
    queries.Add(s1);
    ISqlQuery s2 = _factory.CreateSqlQuery("Select * from Employee where EmployeeType=2", "LoadAbstract.ParallelLoadItems2", typeof(Manager));
    queries.Add(s2);
    ISqlQuery s3 = _factory.CreateSqlQuery("Select * from Employee where EmployeeType=3", "LoadAbstract.ParallelLoadItems3", typeof(DistrictManager));
    queries.Add(s3);

     IList<IEmployee> data = loader.ParallelLoadAbstractItems<IEmployee>(_factory, queries);