LLBLGen MVC?

Posts   
 
    
jaschag
User
Posts: 79
Joined: 19-Apr-2006
# Posted on: 14-Sep-2006 14:50:59   

I am evaluating version 2 and have a couple of higher level questions:

1) What is the best practice pattern for the standard select - open - edit - save/cancel type use case?

E.g. user is presented with a list of entities, selects one for viewing / editing via an edit form, presses save or cancel button when finished. Assume, for example, a winforms SDI app where the entity list is a grid view and the edit form is a winform that pops up when the user chooses to edit the selected row. The behaviour I would expect to see would be that the editing is done in an isolated context such that edits to the entity (or its related entities) are not reflected elsewhere in the app. (e.g. the selection grid) until the user presses save. Issues that need to be considered here include how to perform a shallow / deep copy of the entity to be edited and how to "freshen" other instances of the updated entities following a successful save (preferably without hitting the database unnecessarily).

2) Has anyone considered / created a framework that achieves something along the lines of a hybrid of the self-servicing and adapter paradigms?

I can see that the adapter paradigm provides greater control and flexibility but at some cost in terms of complexity so I am wondering whether it would be possible to extend or add a layer (possibly a specialised controller) on top of an adapter-based model that performs lazy instantiation in a way that mirrors use case requirements. Take, for example, a view / edit form for a large logical entity, e.g. customer, in a smart client that has tabs for the main logical related sub entities i.e. tabs for contacts, orders, invoices, promotional activities etc. Often a user will open this form for a particular purpose e.g to view the order history, so the other tabs will not be opened. In this case it would be wasteful and potentially slow to pre-fetch the entire logical entity as would be common practice in the adapter approach. This additional layer could detect that the view is trying to access a logical sub entity and pre-fetch the associated hierarchy transparently as required. This would avoid the view having to know about data retrieval and allow the granularity of the data access to be optimised to suit purpose i.e. somewhere in between one large potentially wasteful data retrieval and the "chatty" inefficiency of the many retrievals that one would see in a self-servicing scenario.

Both the above seem to suggest some sort of specialised MVC framework for LLBLGen - does such a thing exist?

TIA,

Jascha

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39749
Joined: 17-Aug-2003
# Posted on: 15-Sep-2006 10:36:09   

jaschag wrote:

I am evaluating version 2 and have a couple of higher level questions:

1) What is the best practice pattern for the standard select - open - edit - save/cancel type use case?

E.g. user is presented with a list of entities, selects one for viewing / editing via an edit form, presses save or cancel button when finished. Assume, for example, a winforms SDI app where the entity list is a grid view and the edit form is a winform that pops up when the user chooses to edit the selected row. The behaviour I would expect to see would be that the editing is done in an isolated context such that edits to the entity (or its related entities) are not reflected elsewhere in the app. (e.g. the selection grid) until the user presses save. Issues that need to be considered here include how to perform a shallow / deep copy of the entity to be edited and how to "freshen" other instances of the updated entities following a successful save (preferably without hitting the database unnecessarily).

You could use the SaveFields() /RollbackFields() methods on an entity here simple_smile

2) Has anyone considered / created a framework that achieves something along the lines of a hybrid of the self-servicing and adapter paradigms?

I can see that the adapter paradigm provides greater control and flexibility but at some cost in terms of complexity so I am wondering whether it would be possible to extend or add a layer (possibly a specialised controller) on top of an adapter-based model that performs lazy instantiation in a way that mirrors use case requirements. Take, for example, a view / edit form for a large logical entity, e.g. customer, in a smart client that has tabs for the main logical related sub entities i.e. tabs for contacts, orders, invoices, promotional activities etc. Often a user will open this form for a particular purpose e.g to view the order history, so the other tabs will not be opened. In this case it would be wasteful and potentially slow to pre-fetch the entire logical entity as would be common practice in the adapter approach. This additional layer could detect that the view is trying to access a logical sub entity and pre-fetch the associated hierarchy transparently as required. This would avoid the view having to know about data retrieval and allow the granularity of the data access to be optimised to suit purpose i.e. somewhere in between one large potentially wasteful data retrieval and the "chatty" inefficiency of the many retrievals that one would see in a self-servicing scenario.

The lack of lazy loading requires a different way of working with data, admitted, however adding lazy loading to adapter, makes it effectively selfservicing, so why then still use adapter?

Frans Bouma | Lead developer LLBLGen Pro
Max avatar
Max
User
Posts: 221
Joined: 14-Jul-2006
# Posted on: 15-Sep-2006 12:14:35   

Otis wrote:

The lack of lazy loading requires a different way of working with data, admitted, however adding lazy loading to adapter, makes it effectively selfservicing, so why then still use adapter?

Multiple DB support? simple_smile

But today I'm fine with adapter, I need the control over fetch/save that only adapter can give sunglasses

jaschag
User
Posts: 79
Joined: 19-Apr-2006
# Posted on: 15-Sep-2006 13:07:06   

Thanks for the reply to a rather long-winded post!

Otis wrote:

You could use the SaveFields() /RollbackFields() methods on an entity here simple_smile

These would go some way to achieving the effect but since they operate on the same instance, all changes made during editing would still be seen in any bound controls outside the edit form e.g. the selection grid which is not ideal. I guess they would also need to be called on all related entities as well. I think the general idea of I am looking for would be along the lines:


EntityToBeEdited = EntitySelectedForEditing.Clone(DeepCopy);
if (EditForm.Edit(EntityToBeEdited) == EditFormResult.UserSavedEdits)
{
    EntitySelectedForEditing.Assign(EntitySelectedForEditing, DeepAssignment);
}

where Assign (from old Delphi days) copies the state from one instance to another. Could the above (Clone and Assign) be achieved with serialisation? Is there any state that is not serialised that would need to be taken account of?

Otis wrote:

The lack of lazy loading requires a different way of working with data, admitted, however adding lazy loading to adapter, makes it effectively selfservicing, so why then still use adapter?

Being new to LLBLGen, my understanding of it may be limited but my impression is that the adapter methodology provides more control over what is retrieved and when. Assuming that the hooks are available in the entity2 base classes to detect a request for an unpopulated relation, it would be possible to prefetch defined branches of a logical entity's hierarchy as required. I.e. something along the lines:


// in Customer entity
protected override Orders GetOrders()
{
  if (NotFetchedYet(_orders))
  {
    adapter.Fetch(_orders, PreFetch.OrderDetails, PreFetch.Products);
  }
  return _orders;
}

I realise that this example is very simplistic (and that the functionality would probably be encapsulated in another object that contains the adapter) but I hope it gives an idea of what I am trying to achieve rather than how best to achieve it. Is it possible to hook into the property retrieval call chain for relation properties and test whether the property is unfetched? And then either fetch the hierarchy or provide a prefetch structure? Or am I barking up the wrong tree?confused

Jascha

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39749
Joined: 17-Aug-2003
# Posted on: 18-Sep-2006 10:26:36   

jaschag wrote:

Thanks for the reply to a rather long-winded post!

Otis wrote:

You could use the SaveFields() /RollbackFields() methods on an entity here simple_smile

These would go some way to achieving the effect but since they operate on the same instance, all changes made during editing would still be seen in any bound controls outside the edit form e.g. the selection grid which is not ideal. I guess they would also need to be called on all related entities as well.

You are right about that.

I think the general idea of I am looking for would be along the lines:


EntityToBeEdited = EntitySelectedForEditing.Clone(DeepCopy);
if (EditForm.Edit(EntityToBeEdited) == EditFormResult.UserSavedEdits)
{
    EntitySelectedForEditing.Assign(EntitySelectedForEditing, DeepAssignment);
}

where Assign (from old Delphi days) copies the state from one instance to another. Could the above (Clone and Assign) be achieved with serialisation? Is there any state that is not serialised that would need to be taken account of?

You could serialize it indeed, all state required is serialized as well. What's better: the ObjectID's (to identify an instance) are also serialized so you can eventually use these to find back an entity.

You could use a Context to update the entities after your popup screen closes. First clone, then assign the original entities to a context, then open your popup, do the editing etc. when the edit is done, you simply do a Get of the entities changed from the context, and the fields of the original instance are updated automatically.

Otis wrote:

The lack of lazy loading requires a different way of working with data, admitted, however adding lazy loading to adapter, makes it effectively selfservicing, so why then still use adapter?

Being new to LLBLGen, my understanding of it may be limited but my impression is that the adapter methodology provides more control over what is retrieved and when. Assuming that the hooks are available in the entity2 base classes to detect a request for an unpopulated relation, it would be possible to prefetch defined branches of a logical entity's hierarchy as required. I.e. something along the lines:


// in Customer entity
protected override Orders GetOrders()
{
  if (NotFetchedYet(_orders))
  {
    adapter.Fetch(_orders, PreFetch.OrderDetails, PreFetch.Products);
  }
  return _orders;
}

I realise that this example is very simplistic (and that the functionality would probably be encapsulated in another object that contains the adapter) but I hope it gives an idea of what I am trying to achieve rather than how best to achieve it. Is it possible to hook into the property retrieval call chain for relation properties and test whether the property is unfetched? And then either fetch the hierarchy or provide a prefetch structure? Or am I barking up the wrong tree?confused Jascha

You could do that, the properties are virtual, though it requires a subclass of the entity. Though selfservicing gives you control as well, you just have to do abit more work perhaps here and there.

You could also opt for placing the control of 'lazy loading' related entities of a given entity in a method inside the entity, thus an extra method, like GetOrders(), as Adapter entities don't have methods which return collections or related entities, like GetOrders. For databinding it then is a bit cumbersome, as databinding simply reads hte 'Orders' property.

Frans Bouma | Lead developer LLBLGen Pro
jaschag
User
Posts: 79
Joined: 19-Apr-2006
# Posted on: 18-Sep-2006 12:37:28   

Otis wrote:

You could serialize it indeed, all state required is serialized as well. What's better: the ObjectID's (to identify an instance) are also serialized so you can eventually use these to find back an entity.

You could use a Context to update the entities after your popup screen closes. First clone, then assign the original entities to a context, then open your popup, do the editing etc. when the edit is done, you simply do a Get of the entities changed from the context, and the fields of the original instance are updated automatically.

That sounds exactly what I was looking for - I had a feeling that the Context had a role to play here. On the subject of Context, your docs suggest using it for short periods but do not go into great detail about why. Many other OR frameworks have a context-like container at the heart of their operation which may also provide a single instancing service - is there a reason why we should not use the LLBLGen context in a similar fashion?

Otis wrote:

You could do that, the properties are virtual, though it requires a subclass of the entity. Though selfservicing gives you control as well, you just have to do abit more work perhaps here and there.

You could also opt for placing the control of 'lazy loading' related entities of a given entity in a method inside the entity, thus an extra method, like GetOrders(), as Adapter entities don't have methods which return collections or related entities, like GetOrders. For databinding it then is a bit cumbersome, as databinding simply reads hte 'Orders' property.

I think the responsibility for controlling the lazy loading needs to be in an external object since what (if anything) should be (pre)fetched is dependent on the context in which the entity is being used and should therefore adapt to the circumstances. Perhaps the simplest solution would be to add a virtual method / event pair for each relation to the class that could be hooked as necessary, e.g.


    protected virtual void OnFetchOrders(EntityCollection<OrderEntity> orders);

    public event FetchOrdersEventHandler FetchOrders...

    public virtual EntityCollection<OrderEntity> Order
    {
    get
    {
        if(_order==null)
        {
            _order = new EntityCollection<OrderEntity>(new OrderEntityFactory());
            _order.SetContainingEntityInfo(this, "Customer");
            OnFetchOrders(_order);
        }
        return _order;
    }
    }

Do you think this might work and would this be an easy addition to the code generation template?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39749
Joined: 17-Aug-2003
# Posted on: 19-Sep-2006 10:51:36   

[quotenick="jaschag"]

Otis wrote:

You could serialize it indeed, all state required is serialized as well. What's better: the ObjectID's (to identify an instance) are also serialized so you can eventually use these to find back an entity.

You could use a Context to update the entities after your popup screen closes. First clone, then assign the original entities to a context, then open your popup, do the editing etc. when the edit is done, you simply do a Get of the entities changed from the context, and the fields of the original instance are updated automatically.

That sounds exactly what I was looking for - I had a feeling that the Context had a role to play here. On the subject of Context, your docs suggest using it for short periods but do not go into great detail about why. Many other OR frameworks have a context-like container at the heart of their operation which may also provide a single instancing service - is there a reason why we should not use the LLBLGen context in a similar fashion? The llblgen pro context shouldn't be compared with a context / session object you see a lot in other o/r mapper frameworks. These contexts/session objects are a cache and dataaccessadapter combined. LLBLGen Pro doesn't use such a cache object as it uses tracking of changes inside the entities so there are less problems with entities coming from other tiers etc. etc. The context in llblgen pro is to make uniquing possible, thus just one instance of an entity type for each entity data loaded from the DB. This isn't always necessary, mostly you won't care about it, but sometimes you do. It can then be necessary to allow multiple uniquing contexts as well, with the Context you can. So it's not comparable with the other contexts/sessions you might be familiar with.

Otis wrote:

You could do that, the properties are virtual, though it requires a subclass of the entity. Though selfservicing gives you control as well, you just have to do abit more work perhaps here and there.

You could also opt for placing the control of 'lazy loading' related entities of a given entity in a method inside the entity, thus an extra method, like GetOrders(), as Adapter entities don't have methods which return collections or related entities, like GetOrders. For databinding it then is a bit cumbersome, as databinding simply reads hte 'Orders' property.

I think the responsibility for controlling the lazy loading needs to be in an external object since what (if anything) should be (pre)fetched is dependent on the context in which the entity is being used and should therefore adapt to the circumstances. Perhaps the simplest solution would be to add a virtual method / event pair for each relation to the class that could be hooked as necessary, e.g.


    protected virtual void OnFetchOrders(EntityCollection<OrderEntity> orders);

    public event FetchOrdersEventHandler FetchOrders...

    public virtual EntityCollection<OrderEntity> Order
    {
    get
    {
        if(_order==null)
        {
            _order = new EntityCollection<OrderEntity>(new OrderEntityFactory());
            _order.SetContainingEntityInfo(this, "Customer");
            OnFetchOrders(_order);
        }
        return _order;
    }
    }

Do you think this might work and would this be an easy addition to the code generation template?

This might work indeed, and the addition is rather simple for you. Open the file Templates\SharedTemplates\Net2.x\entityIncludeAdapter.template in template studio or other editor and go to line 543, where the one to many loop is located and the property is declared. So inside that loop you've to add your other declarations.

Frans Bouma | Lead developer LLBLGen Pro
rdhatch
User
Posts: 198
Joined: 03-Nov-2007
# Posted on: 17-Jan-2009 17:05:34   

jaschag -

Did you ever get your Cancel functionality to work? And if so - Could you please give me a small code example of how you updated your original entity using the Context?

Much appreciated! Thanks!

Ryan D. Hatch

Here's what I'm trying to achieve: http://llblgen.com/TinyForum/Messages.aspx?ThreadID=11947&StartAtMessage=0&#84113