Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 927
Robert Massa Avatar asked Jan 28 '26 08:01

Robert Massa


2 Answers

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 { }
like image 200
Justin Niessner Avatar answered Jan 30 '26 21:01

Justin Niessner


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 :)

like image 38
cwap Avatar answered Jan 30 '26 22:01

cwap