Gennadiy Donchyts Just another WordPress weblog

3Feb/100

Is it good or bad practice to use events in Domain Classes?

Why to use events in entities?

.NET provides a native implementation of observer / subscriber pattern by means of events. I tried to use them not only in the UI classes but also to manage changes in the entities (see info about DDD for a definition of an entity). A lot of Entities in our application are in most cases bound to one or many controls. Entities are composed with other entities, they get changed, all these changes need to be monitored so that corresponding views will be updated or any other actions will be invoked.

Events in .NET is something to be used very carefully. Otherwise you'll end up with too many events :) . Finally we ended up with the two types of the events which are used in the entities and can be considered as Domain Events:

1. NotifyPropertyChanged - fired when any property changes, e.g. when name of the branch is changed - tree node needs to be updated, if branch is selected - it's name in the property grid control needs to be updated, etc.

2. NotifyCollectionChanged - fired when any collection is changed, add/remove item

Definition of Domain Event comes from the following post of Martin Fowler: http://martinfowler.com/eaaDev/DomainEvent.html

Once domain model is fully covered by events,  implementation of Undo / Redo functionality becomes trivial task (Memento pattern + these 2 types of events).

After a few tries we ended up with the following solution:

  • All objects implement INotifyPropertyChanged for all entities
  • All collections used in the properties implement INotifyCollectionChange, using interface IEventedList<T>: IList<T>, INotifyCollectionChanged
  • When events are fired in the child objects - they are bubbled to the higher level object

Of course implementing it by hand in every domain class is a violation of the DRY principle, so PostSharp were used to implement such behavior automatically by adding a few attributes on top of the entity class.

Example

Let's look on a very simple example, a blog containing blog posts and comments.

Without events it may look like this:

When events are used - there is simply no need for controller, everything can be bound directly to the view controls:

In this case view (or a very simple controller) subscribes to the changes in the domain model and then refreshes what needs to be refreshed.

Of course in reality there are some more issues, like:

  • Clients need to subscribe only to the events they need to use (filter)
  • Memory leak issues, solution: use weak events where possible http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
  • Sometimes events need to be grouped or filtered (10000 items added to collection will generate 10000 events and therefore 10000 calls, if there are no subscribers - overhead is not that big, ~20% compare to the plain calls)

But in any case if events are used in as shown here - much less source code need to be produced plus data model always remain in sync with all views.

How events are fired and handled when blog title changes:

How events are fired and routed when new post is added:

Filed under: Uncategorized No Comments
10Dec/090

Just learned from ReSharper how to make code look nicer

ReSharper really helps to learn how to code. During writing a test for a ForEach extension method for IEnumerable<T> it suggested to improve the following code:

ReSharper_group1

The resulting code got much better:

ReSharper_group2

6Dec/090

Got stack overflow on StackOverflow :)

StackOverflow

Filed under: Uncategorized No Comments
1Dec/091

Using FitNesse under .NET without JRE

Not sure if anyone will use it but it was kind of funny thing to do. After playing with FitNesse (Function Integration Testing Framework) which is mainly developed in Java I check if it can be converted to .NET using IKVM and the trick seems to work, you can start server, edit pages and runs tests. It is simply packaged version of fitnesse.jar and .NET runner by by Mike Stockdale. ... uses pure Java version.

Download: fitnesse4net.zip 8.7Mb

Zip file contains only .NET binaries required to run FitNesse and small demo project containing a single class. No Java is required.

After starting run_server.cmd you have to wait until FitNesse will unpack all it's files into FitNesseRoot, then press refresh in browser. After you run demo test on a root page you should get something like this (the web page has been updated, see another blog post for a new version):

fitnesse-results

It works a bit slower compare to Java but still seems to be stable.

Directory structure:

fit4net-directory

fit4net
|
+---bin
|       fit.dll
|       fitnesse.exe ................ FitNesse all in one wiki/server
|       fitSharp.dll
|       IKVM.OpenJDK.Core.dll
|       IKVM.OpenJDK.Misc.dll
|       IKVM.OpenJDK.Security.dll
|       IKVM.OpenJDK.Text.dll
|       IKVM.OpenJDK.Util.dll
|       IKVM.OpenJDK.XML.dll
|       IKVM.Runtime.dll
|       Runner.exe .................. command-line test runner, fetches content from server
|       RunnerW.exe ................. same as previous, but with window
|
\---example
|   run_server.cmd
|
+---FitNesseRoot
|       content.txt
|       properties.xml
|
\---src
|   DemoFitNesseTests.csproj
|   DemoFitNesseTests.sln
|   DemoMultiplierBy2.cs
|
\---bin
\---Debug
DemoFitNesseTests.dll

Right now I'm struggling trying to integrate it with MSBuild / TeamCity, looks like nobody implemented it yet :( DONE

Filed under: Uncategorized 1 Comment
31Jul/090

Making NHibernate lazy-loading PostSharp-ed objects work.

I'm using PostSharp to add implementation of INotifyPropertyChanged in AOP way using PostSharp compound aspect.

At the same time object has to be saved into a database and loaded in a lazy way (NHibernate + LinFu as a DynamicProxy). It seems that when object is loaded - all aspects disappear in a proxy object created by a DynamicProxy. All required fields are present in the proxy type but aspects are not initialized and PostSharp  instanceCredentials = {0}.

Gael Fraiteur sent me a message after I Twittered about the problem (so, Twitter works! :) ):

@gena_d Look at http://tiny.cc/zIXFT. Call this method: LaosUtils.InitializeCurrentAspects() if the aspect constructor is skipped.

Thanks! But unfortunatelly it was not a solution for me since object even has no aspect implementation injected into it, it happens in the constructor which is skipped :( .

QuickWatch_LazyAOPObject

After debugging deep into NHibernate and LinFu I've found a workaround:

  1. Listen to the ILoadEventListener when lazy object is initialized by NHibernate
  2. During load, inject another instance of NotifyPropertyChangedImplementation into proxy and forward event from underlying object to that implementation.
public void OnLoad(LoadEvent @event, LoadType loadType)
{
    defaultLoadEventListener.OnLoad(@event, loadType);

    InitializePropertyChangedAspect(@event.Result);
}

///
/// Connects property changed events of the proxy object to the real object
/// so when PropertyChanged event is fired - it will be redirected to the proxy.
///
///

private void InitializePropertyChangedAspect(object o)
{
    if (!(o is INHibernateProxy) || !(o is IComposed))
    {
        return;
    }

    var proxyObject = (INHibernateProxy) o;
    var lazyInitializer = proxyObject.HibernateLazyInitializer;
    var realObject = lazyInitializer.GetImplementation();

    // initialized property changed aspect implementation
    // and redirects events fired in the wrapped object
    // to the aspect implementation in the parent object (proxy)
    var aspect = new NotifyPropertyChangedImplementation(o, InstanceCredentials.Null, false);
    ((IComposed) o).SetImplementation(InstanceCredentials.Null, aspect);

    var notifiable = (INotifyPropertyChanged) realObject;
    notifiable.PropertyChanged += ((sender, e) => aspect.OnPropertyChanged(proxyObject, e));
}

As a result a client code subscribing to a proxy object can listen to a PropertyChanged event. The code looks a bit ugly, it would nicer if DynamicProxy can handle these events.

After implementation of aspect is injected - the proxy object looks like this:

Filed under: Uncategorized No Comments
30Mar/091

SPAM

Или как же меня достало удалять эти комменты, кто бы им интернет зафайерволил в конце-концов, поставил вот что-то под названием clickacha, чтобы им жизнь немного усложнить.

update: clickacha не работает, удалил и поставил обычную Capatcha

Filed under: Uncategorized 1 Comment