当前位置: 动力学知识库 > 问答 > 编程问答 >

c# - Should one extend or encapsulate ORM objects?

问题描述:

I'm having trouble understanding how to use ORM generated objects. We're using LLBLGen for mapping our database model to objects. These objects we encapsulate in another layer which represents our business model(I think).

Maybe this bit of code will explain this better.

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

}

Exposing the entity's fields like properties feels awkward, since I'm mapping all over again. With list-types it's even much worse, since I have to write a "Add" and "Remove" method plus a property that exposes List.

In the above example in the BookType setter I need access to the BookTypeEntity object, I can get this object by instantiating a new one using the ID oh the BookType object. This also doesn't feel good.

I'm wondering if I shouldn't just extend the BookEntity object and add my business logic there? Or maybe use partials?

In the LLGLGen examples they use the entity objects directly, but this looks very messy to me. I want to have objects which can also have methods for my business logic(like CountPages) in the code above.

网友答案:

Dunno if it's possible in LLGLGen, but what I generally do when working with ORMs is to create an interface to the persisted class, in your example IBook. I expose this interface via a public getter from the wrapping class. This way, if needs will be, you can extend you IBook the way you want if you need to add some custom behaviour to its fields.

Generally speaking, I think there's 3 ways of "mapping" your ORM-entities to your domain:

  1. The way you've posted. Basically, remap everything again
  2. The way I posted, expose the ORM-entity as an interface
  3. Expose the ORM-entity directly

I don't like #1, cause I don't like to have 2 mappings in my application. DRY, KISS and YAGNI are all violated by this.

I don't like #3 cause it would make whatever consumer-layer of your domain-layer talk directly to the ORM layer.

.. So, I go with #2, as it seems to be the lesser of 3 evils ;)

[Update] Small code snippet :)

ORM-generated class in the data-layer:

public class Book : IBook
{
   public string ISBN {get; private set;}
}

IBook is found in the business-logic layer, along with a book wrapper:

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
    }
}

I don't know if this is a recognizable design-pattern, but it's what I use, and so far it has worked quite well :)

网友答案:

I've never used LLBLGen for mapping, but most of the ORM tools I've worked with generate partial classes. I then add any custom code/logic I'd like to add to those objects in a seperate file (so they don't get over-written if the partial classes are re-generated).

Seems to work pretty well. If you don't get partial classes from your ORM, I'd create a Facade which wraps your Data Object with your Business Logic...that way the two are seperated and you can re-gen one without overwriting the other.

UPDATE

Partial classes support implementing an Interface in one declaration of a partial class and not the other. If you want to implement an interface, you can implement it in your custom code partial file.

Straight from MSDN:

partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }

is equivilant to

class Earth : Planet, IRotate, IRevolve { }
网友答案:

You are feeling the pain of the mismatch between the different paradigms of relational data and objects.

By this, I mean that the worlds of relational data and objects are very, very different from each other. For example, in database-land all data is public. In object-land, data is encapsulated and very specifically not made public. In database-land, all relationships are bi-directional, whereas in object-land an object in a collection might not have any reference to its parent. In database-land, procedures are global. In object-land, procedures are local to the object which contains the acted-upon data.

For these reasons and more, an approach which creates objects that represent database tables is inherently going to be painful. While yes, technically they are objects, they have the semantics of database-land. Making them live in object-land, as you have experienced, is difficult if not impossible. This can be referred to as data-first.

A better approach (in my opinion) is to focus on mapping objects to the database, rather than mapping the database to objects. This can be referred to as object-first, and is supported very well by NHibernate. This approach emphasizes the fact that a database is an implementation detail of a system, not a design precept.

I realize this doesn't specifically answer your question, but I hope it provides some context as to why you are having a hard time conceptualizing your entities: they are tables first and entities second.

分享给朋友:
您可能感兴趣的文章:
随机阅读: