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:
Fitnesse4net source code is available on Google Code
I've uploaded source code for .NET version of FitNesse4Net to Google Code. It is simply original version of FitNesse but converted to .NET using IKVM.NET and then extended with a small wrapper allowing to use default test runner to run .NET tests. Also command-line is a bit extended so that it can run as a server or command-line runner. See source code and binaries here: FitNesse4Net on Google Code
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:

The resulting code got much better:

Update version of FitNesse for .NET.
This time a command-line and MSBuild runners are included: fitnesse4net-bin.zip 8.3Mb
After experimenting a bit more with FitNesse I managed to make it run .NET assemblies with about 15Kb of source code injected directly into FitNesse internals
. It not tested much so probably some bugs will need to be fixed, but it is able to run demo .NET assemblies without any problems. The main purpose was proof of concept, to see if it will work at all and try to develop a MSBuild tasks allowing to run all tests using command-line.
What is included:

DemoTest1 page contains the following text:
!contents
!|script|DemoProject.MultiplierBy2|
|input|2|
|run|
|check|output|4|
!|script| DemoProject.MultiplierBy2|
|input|2|
|run|
|check|output|5|
The file content.txt in FitNesseRoot/ is used to find a list of assemblies to be referenced, looks like this:
!path ..\DemoProject\bin\Debug\DemoProject.dll
After run of run-test-suite.cmd the following output should be printed and the target/ directory should contain results of the tests as html.
Loading assembly ..\DemoProject\bin\Debug\DemoProject.dll ...
Running test suite FrontPage.DemoSuite ...
Running test FrontPage.DemoSuite.DemoTest1 ...
Test results: 3 right, 1 wrong, 0 ignored, 0 exceptions
Running test FrontPage.DemoSuite.DemoTest2 ...
Test results: 4 right, 0 wrong, 0 ignored, 0 exceptions
Contents of FitNesse.proj:
<Project>
<UsingTask TaskName="Tasks.RunSlimTestSuite" AssemblyFile="..\bin\FitNesse4Net.MSBuidTasks.dll" />
<UsingTask TaskName="Tasks.RunSlimTest" AssemblyFile="..\bin\FitNesse4Net.MSBuidTasks.dll" />
<Target Name="RunSlimTestSuite">
<RunSlimTestSuite WorkingDirectory=".\src\DemoProject.Tests" TargetDirectory=".\target" TestSuiteName="FrontPage.DemoSuite" />
</Target>
<Target Name="RunSlimTest">
<RunSlimTest WorkingDirectory=".\src\DemoProject.Tests" TargetDirectory=".\target" TestName="FrontPage.DemoSuite.DemoTest1" />
<RunSlimTest WorkingDirectory=".\src\DemoProject.Tests" TargetDirectory=".\target" TestName="FrontPage.DemoSuite.DemoTest2" />
</Target>
</Project>
msbuild-run-test-suite.cmd:
C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe FitNesse.proj /t:RunSlimTest
msbuild-run-test.cmd:
C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe FitNesse.proj /t:RunSlimTestSuite
run-test-suite.cmd:
..\bin\FitNesse4Net.exe --run-test-suite=FrontPage.DemoSuite --work-directory=.\src\DemoProject.Tests --target-directory=.\target
run-test.cmd:
..\bin\FitNesse4Net.exe --run-test=FrontPage.DemoSuite.DemoTest1 --work-directory=.\src\DemoProject.Tests --target-directory=.\target
start-server.cmd:
start ..\bin\FitNesse4Net.exe --port=8080 --work-directory=.\src\DemoProject.Tests
stop-server.cmd:
..\bin\FitNesse4Net.exe --shutdown --port=8080
After FitNesse4Net.exe is started without arguments, usage help will be printed to the console. Many options are similar to those provided by Java fitnesse.jar but some are new like --shutdown, --run-test-suite, --run-test
Note: after run of start-server.cmd - it will generate a lot of other files in your FitNesseRoot.
After FitNesse4Net.exe is stared, a list of possible options will be printed to the console. Many options a similar to those provided by Java fitnesse.jar but some are new like --shutdown, --run-test-suite, --run-test
TODO: check if it will work as a test runner from FitNesse wiki (currently it will say that runner is not found if you try to run tests from the fitnesse wiki).
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):

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

Right now I'm struggling trying to integrate it with MSBuild / TeamCity, looks like nobody implemented it yet
DONE
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
.
After debugging deep into NHibernate and LinFu I've found a workaround:
- Listen to the ILoadEventListener when lazy object is initialized by NHibernate
- 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:

SPAM
Или как же меня достало удалять эти комменты, кто бы им интернет зафайерволил в конце-концов, поставил вот что-то под названием clickacha, чтобы им жизнь немного усложнить.
update: clickacha не работает, удалил и поставил обычную Capatcha
Upgraded jar2ikvmc to ikvm 0.38.0.2
Just upgraded jar2ikvmc to the last version of ikvm. The most annoying problem was with conversion of JarAnalyzer to .NET, a resource file called Filter.properties was missing. The problem was solved after I embedded it by hand into JarAnalyzer.jar and then converted using ikvmc.exe.
The new version is uploaded to jar2ikvmc on Google code page.
Minor release. Moved to Google Code.
Today I moved all sources and binaries to Google Code. It seems to work fine for such a small tool
.
Please use Issues section there to submit bug fixes or suggestions.
Also a new release was created combined with latest version of ikvm: ikvmbin-0.37.2855.zip.
From now tool will be distributed using the following naming convention:
jar2ikvmc-<major>.<minor>.<build>.zip
Build number is actually equal to tagged revision number in subversion.
Binary: jar2ikvmc-0.1.7.zip
Source: jar2ikvmc-0.1.7-src.zip










