我无法理解如何使用ORM生成的对象。我们使用LLBLGen将数据库模型映射到对象。我们将这些对象封装在代表我们业务模型的另一层中(我认为)。
也许这段代码会更好地解释这一点。
public class Book { // The class as used in our application
private BookEntity book; // LLBLGen entity
private BookType bookType; // BookType is another class that wraps an entity
public Book(int Id) {
book = new BookEntity(Id);
}
public BookType BookType {
get { return this.bookType; }
set {
this.bookType = value;
this.book.BookType = new BookTypeEntity(value.ID);
this.book.Save();
}
}
public int CountPages() { } // Example business method
}
公开像属性这样的实体字段感觉很尴尬,因为我正在重新映射。对于列表类型,它甚至更糟,因为我必须编写一个“添加”和“删除”方法以及一个公开List的属性。
在BookType setter中的上述示例中,我需要访问BookTypeEntity对象,我可以通过使用BookType对象的ID实例化一个新对象来获取此对象。这也感觉不太好。
我想知道我是否应该只扩展BookEntity对象并在那里添加我的业务逻辑?或者也许使用偏见?
在LLGLGen示例中,他们直接使用实体对象,但这看起来非常麻烦。我想在上面的代码中拥有可以为我的业务逻辑(如CountPages)提供方法的对象。
答案 0 :(得分:5)
我从未使用LLBLGen进行映射,但我使用的大多数ORM工具都会生成部分类。然后我添加任何自定义代码/逻辑,我想在一个单独的文件中添加到这些对象(因此如果重新生成部分类,它们不会被覆盖)。
似乎工作得很好。如果你没有从你的ORM获得部分类,我会创建一个Facade,它将你的数据对象与你的业务逻辑包装在一起...这样两者是分开的,你可以重新生成一个而不会覆盖另一个。
<强>更新强>
部分类支持在一个部分类的声明中实现接口,而不支持另一个。如果要实现接口,可以在自定义代码部分文件中实现它。
直接来自MSDN:
partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }
等同于
class Earth : Planet, IRotate, IRevolve { }
答案 1 :(得分:3)
Dunno如果在LLGLGen中是可能的,但是在使用ORM时我通常做的是创建一个持久化类的接口,在你的示例中是IBook。我通过包装类中的公共getter公开了这个接口。这样,如果需要,您可以根据需要向您扩展IBook,如果您需要在其字段中添加一些自定义行为。
一般来说,我认为有三种方法可以将您的ORM实体“映射”到您的域名中:
我不喜欢#1,因为我不喜欢在我的应用程序中有2个映射。 DRY,KISS和YAGNI都受到了侵犯。
我不喜欢#3因为它会使你的域层的任何消费者层直接与ORM层对话。
..所以,我选择#2,因为它似乎是3个邪恶中较小的一个;)
[更新]小代码片段:)
数据层中的ORM生成的类:
public class Book : IBook
{
public string ISBN {get; private set;}
}
IBook位于业务逻辑层,还有一个书籍包装器:
public interface IBook
{
string ISBN {get;}
}
public class BookWrapper //Or whatever you want to call it :)
{
//Create a new book in the constructor
public BookWrapper()
{
BookData = new Data.MyORM.CreateNewBook();
}
//Expose the IBook, so we dont have to cascade the ISBN calls to it
public IBook BookData {get; protected set;}
//Also add whichever business logic operation we need here
public Author LookUpAuther()
{
if (IBook == null)
throw new SystemException("Noes, this bookwrapper doesn't have an IBook :(")
//Contact some webservice to find the author of the book, based on the ISBN
}
}
我不知道这是否是一个可识别的设计模式,但它是我使用的,到目前为止它已经运作得很好:)。
答案 2 :(得分:1)
您感受到关系数据和对象的不同范式之间不匹配的痛苦。
通过这个,我的意思是关系数据和对象的世界彼此非常非常不同。例如,在数据库中,所有数据都是公开的。在object-land中,数据被封装,并且非常明确地不公开。在database-land中,所有关系都是双向的,而在object-land中,集合中的对象可能没有对其父级的任何引用。在数据库领域,程序是全球性的。在object-land中,过程是对象的本地,其中包含代理数据。
由于这些原因以及更多,创建表示数据库表的对象的方法本身就很痛苦。虽然是,但从技术上讲它们是对象,它们具有数据库 - 土地的语义。如你所经历的那样,让它们生活在物体土地上,即使不是不可能也是困难的。这可以称为数据优先。
更好的方法(在我看来)是专注于将对象映射到数据库,而不是将数据库映射到对象。这可以称为 object-first ,NHibernate支持得很好。这种方法强调数据库是系统的实现细节,而不是设计规则。
我意识到这并没有具体回答你的问题,但我希望它提供了一些关于为什么你很难概念化你的实体的背景:它们首先是表,而实体是第二。