4

my first real (not test) NHibernate/Castle.ActiveRecord project is developing quickly. I am working with NHibernate/Castle.ActiveRecord about one month now but still have not a real idea how to handle Sessions in my WindowsForms application.

The common handling-methods seam not to work for me:

  • SessionPerRequest, SessionPerConversation, etc. all only work for WebApplications, etc.
  • SessionPerApplication is not recomanded/highly dangerous when I am correct
  • SessionPerThread is not very helpfull, since I either have only one thread, the WindowsForms-thread, or for each button-click a new thread. The first thing would make my applicaton use too much memory and to hold old objects in the memmory. With worker-threads for ech button click I would disable lazy-loading, since my loaded objects would live longer then the thread.
  • SessionPerPresenter is not working as well, because it is common, that I open a "sub-presenter" in a form to let the user search/load/select some referenced objects (foreigen key) and of cause the presenter is destroyed - what means session closed - but the object used in the "super-presenter" to fill the referencing property (foreigen key).

I've used google and bing for hours and read a lot, but only found one good website about my case: http://msdn.microsoft.com/en-us/magazine/ee819139.aspx . There SessionPerPresenter is used, but to a "sub-presenter" it is only given the id, not the entire object! And it seams that there are no foreigen-keys in this example and no scenari in wich a object is returned to a "super-presenter".

Qestions

  1. Is there any other method of session handling for windowsforms/desktop-application?
  2. I could add a session-property or a session-constructor-parameter to all of my presenters, but it feels not right to have session-handling all over my ui-code.
  3. When an Exception occures NHibernate want's me to kill the session. But if it is 'only' a business-logic exception and not an NHibernate-Exception?

Example

I am trying to make an example the covers most of my problem.

// The persisten classes
public class Box
{
  public virtual int BoxId{get;set;}
  public virtual Product Content{get;set;}
  ...
}

public class User
{
  public virtual int UserId{get;set;}
  public virtual IList<Product> AssigenedProducts{get;set;}
  ...
}

public clas Product
{
  public virtual int ProductId{get;set;}
  public virtual string PrductCode{get;set;}
}

.

// The presenter-classes
public class ProductSearchPresenter : SearchPresenter<Product> { ... }
public class ProductEditPresenter : EditPresenter<Product> { ... }
public class UserSearchPresenter : SearchPresenter<User> { ... }
public class UserEditPresenter : EditPresenter<User> { ... }
public class BoxSearchPresenter : SearchPresenter<Box> { ... }
public class BoxEditPresenter : EditPresenter<Box> { ... }
// The search-presenters allow the user to perform as search with criterias on the class defined as generic argument and to select one of the results
// The edit-presenters allow to edit a new or loaded (and given as parameter) object of the class defined as generic argument

Now I have the following use-cases, wich all can be performed in the same application at the same time asyncronous (the use simply switchs between the presenters).

  1. using an instance of BoxSearchPresenter to search and select a object
    1. part of this usecase is to use an instance of the ProductSearchPresenter to fill a criteria of the BoxSearchPresenter
    2. part of this usecase is to use an instance of the BoxEditPresenter to edit and save the selected object of the BoxSearchPresenter-instance
  2. using an instance of UserSearchPresenter to search and select a object
    1. part of this usecase is to use an instance of the UserEditPresenter to edit and save the slected object of the UserSearchPresenter
    2. part of this usecase is to use a ProductSearchPresenter to search and select objects that will be added to User.AssignedProducts.
  3. Using an instance of ProductSearchPresenter to search and select a object.
    1. part of this usecase is to use an instance of ProductEditPresenter to edit and save a selected object of the ProductSearchPresenter.

It's only a small collection of usecases, but there are allready a lot of the problems I have.

  • UseCase 1. and 2. run at the same time in the same ui-thread.
  • UseCase 1.1. and 2.2. return there selected objects to other presenters that use this objects longer then the presenters exist that have loaded the object.
  • UseCase 3.1. might alter a object loaded from 2.2./1.1. before 3.1. was started, but when 2.2./1.1. is commited before 3.1. is finished the object would be saved and it would not be possible to "rollback" 3.1.
Flexo
  • 87,323
  • 22
  • 191
  • 272
Juy Juka
  • 189
  • 1
  • 9

1 Answers1

2

Here is just a short view of what I found best to fit into our WinForms application architecture (based on MVP).

Every presenter is constructor dependent on repositories which it needs, for example if you have InvoicePresenter then you have InvoiceRepository as dependency, but you will probably have CustomerRepository and many others depending on complexity (CustomerRepsitory for loading all customers into the customers combobox if you want to change customer of the invoice, stuff like that).

Then, every repository has a constuctor argument for UnitOfWork. Either you can abstract the session with UnitOfWork pattern, or you can have your reporitories depend on ISession.

Everything is wired together by IoC container, where we create presenters based on "context". This is a very simple concept, context is per presenter and all sub presenter, which in turn we create as composite block of more complex presenters to reduce complexitiy (if for example you have multiple tabs of options to edit some entity or something).

So, in practice, this context is 90% time form based, because one form is at least one presenter / view.

So to answer your questions:

  1. Session per presenter and session per conversation (works with WinForms as well) are only really usable patterns here (and opening closing sessions all over the place, but not really good way to handle that)-

  2. this is best solved by making repositories depend on session, not presenters. You make presenters depend on repositories, repositories depend on session, and when you create all, you give them common session; but as I state again, this is only practical when done in contexts. You cannot share session for presenter editing invoices and another presenter editing customers; but you can share session when editing invoice via main presenter and invoice details and invoice notes sub presenter.

  3. Please clarify, didn't understand this...

Denis Biondic
  • 7,943
  • 5
  • 48
  • 79
  • Hello Denis Biondic, thank you a lot. I understand what you are saying but I am woundering how the IoC-Container should know wich UnitOfWork to inject? Especially in that scenario where customers are edited in one unit-of-work but in the other only loaded. Somehow the IoC-Container would have to know about both unit-of-work objects that are aktive in the RAM at the same time and somehow tell appart when to use wich one. Greetings Juy Juka @Denis Biondic – Juy Juka Jan 15 '12 at 11:52