Showing posts with label Castle Project. Show all posts
Showing posts with label Castle Project. Show all posts

Wednesday, 8 April 2009

Intercepting xaml DataTemplate of a given DataType

Since a few months I am working on a project where we use integrate WPF with and IOC, Castle Windsor (through the extension of the Composite WPF Contrib).

One of our xaml file would look like this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/
xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Presentation="clr-namespace:XXX.Modules.MyForm.Presentation"
xmlns:WinForms="clr-namespace:XXX.Modules.Common.WinForms"
xmlns:Converters="clr-namespace:XXX.Modules.Common.Converters"

<converters:booleanenumconverter key="YYYConverter">
[snipped other converters]
<DataTemplate DataType="{x:Type
Presentation:MyFormPresentationModel}">
[snipped template xaml]
</datatemplate>
</resourcedictionary>
I wrote a couple of interceptors (inheriting from IInterceptor and implementing Intercept), but when I try to use them resolving the presentation model, the xaml DataTemplate resolves to a string (the name of the generated proxy, something as IMyFormPresentationModelProxy1de831fc74a341249b67...), not to the instanced object (which stepping in in debug I can see it is registered and then created and then also proxied correctly, as far as I can understand). This is exactly the same behaviour we get if we try to use the interface (i.e. setting the DataType to "{x:Type Presentation:IMyFormPresentationModel}" we get a string as well instead of the object).

Using the .Net reference source symbols (and/or deblector) I have been trying to step in on the exact bit of code which is setting that string instead of the correct proxied object or even the underlying instanced object, but with no luck so far.

I do think the issue may be with the xaml engine being unable to match the proxied type with the DataType of the DataTemplate, and I found out the .Net framework may perform that using the TypeExtension/MarkupExtension/DataTemplateSelector classes and possibly some implementation of the IXamlTypeResolver interface. From a quick search, I found out nowadays people is using those mostly to provide some support for generics in xaml (see i.e. "How To Get ParserContext" or "Limited generics support in Xaml").

From a purely theoretical point of view, I don't like much the idea to have to modify the xaml files to be able to intercept them, so I am not absolutely sure I have been missing something.

Before trying to spend some time on that (i.e. writing our own TypeExtension), I thought it could be better asking around if there is someone with previous experience or also some insight on this kind of scenario, so I have been posting a similar text to various newsgroups and mailing lists.

Last but not least, although I don't guess this is a Windsor bug (or at last I cannot yet see why it should be :)), we are using Castle project assemblies built from the revision 5380 of the 19th October 2008 in the trunk.

Intercettare/Ereditare il DataType per un DataTemplate in xaml

Da qualche mese lavoro ad un progetto dove usiamo WPF integrato con un IOC container (Castle Windsor, tramite l'estensione fornita dal progetto Composite WPF Contrib).

Abbiamo un file xaml simile a questo pseudo-codice:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/
xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Presentation="clr-namespace:XXX.Modules.MyForm.Presentation"
xmlns:WinForms="clr-namespace:XXX.Modules.Common.WinForms"
xmlns:Converters="clr-namespace:XXX.Modules.Common.Converters"

<converters:booleanenumconverter key="YYYConverter">
[snipped other converters]
<DataTemplate DataType="{x:Type
Presentation:MyFormPresentationModel}">
[snipped template xaml]
</datatemplate>
</resourcedictionary>
Ho scritto un paio di intercettori (in pratica usando un componente chiamato Castle Dynamic Proxy, questi generano delle classi che ereditano i componenti registrati con Windsor e che poi si possono usare per intercettare i metodi prima e dopo la loro esecuzione, ad esempio per scrivere dei log o per fare altre cose), ma quando tento di usarli risolvendo i vari componenti intercettati, i DataTemplate definiti nei file xaml dei componenti non risolvono l'oggetto generato (che e' stato istanziato correttamente da Windsor, come posso ad esempio vedere mentre sono in debug), ma ritornano una stringa, che poi altro non e' che il nome del proxy generato (una cosa tipo IMyFormPresentationModelProxy1de831fc74a341249b67...).

Questo e' un comportamento molto simile a quello che incontro quando tento di usare un'interfaccia come DataType del DataTemplate (ad esempio impostando il DataType ad una cosa come "{x:Type Presentation:IMyFormPresentationModel}"), per cui immagino che il DataType non supporti molto bene l'ereditarieta' (o comunque sia piuttosto conservativo nella risoluzione dei tipi).

Dato che non penso che il problema sia nell'IOC o nell'estensione tramite la quale lo integriamo con WPF, immagino che il problema sia quindi che lo "xaml engine" non sia capace di far corrispondere il tipo descritto nel file xaml con quello generato dall'intercettore.

Ho fatto un po di ricerche, ed ho trovato che c'e' gente che per fare cose probabilmente simili (perlopiu' collegate al fatto che il DataType non supporta pienamente i tipi generici, vedi ad esempio "How To Get ParserContext" oppure "Limited generics support in Xaml") usa classi come TypeExtension o MarkupExtension e possibilmente qualche implementazione dell'interfaccia IXamlTypeResolver. Ho anche trovato qualche accenno al possibile uso di un DataTemplateSelector.

Da un punto di vista puramente teorico, l'idea di dover modificare i file xaml per poter intercettare i DataTemplate non mi attrae particolarmente, quindi mi sto chiedendo se per caso non mi sono perso qualcosa.

Prima di investire altro tempo per risolvere il problema (ad esempio scrivendo una mia TypeExtension), sto cercando di chiedere in vari gruppi o mailing list se c'e' qualcuno che abbia esperienza di questo tipo scenario (o qualcuno simile, magari qualcosa che abbia a che fare con l'ereditarieta' ed i tipi definiti in DataType per DataTemplate).

Anche soltanto qualche suggerimento o qualche referenza sarebbe molto gradita :)

Ho gia' scritto un messaggio molto simile sulla mailing list di Castle, ma immagino che questo non sia un bug di Windsor, ma il risultato di una limitazione (o di una scelta progettuale) del DataType.

Friday, 13 April 2007

Improving build performances: assemblies on the GAC

Working with a Monorail based web project, to improve build performances, I added some of the Castle Project assemblies to the GAC.

Obviously, there is a time when you need a fast build, and there is a time when you need to be sure that all the assemblies your application is using are in the /bin folder, so I wrote 2 batch files to cache and uncache the assemblies listed in 2 text files (some example code at the end of the article).

I tested on my machine with a sizable web application project the effect of having or not having those assemblies on the GAC on the build time, and this are the rounded average results:

Solution
GAC 10"
No-GAC 27"

Web
GAC 5"
No-GAC 22"

That is, on my machine when I put the files on the GAC, when building I gain 17".

[Note: The web build is normally 5" faster than the Solution build because in the solution there are project not directly needed by the Web (In primis the tests, then some data migration and seeding stuff, some productivity measurament tool, and so on).]

Why it happens? My best answer is that having the Castle project assemblies in the GAC means that during the build these are not copied anymore in the various /bin folders.

I do advice you to try to test this in your machines as well, anyway, please read carefully and make sure you understand the next points before moving ahead:

- the build phase generate assemblies from the source code. Those assemblies are bytecode, not executable programs, and if not opportunely precompiled, they will be compiled at the startup of the .Net application. This means that after building, when you will refresh the web page, this will take exactly the same time to answer you as it was taking before putting the Castle Project assemblies on the GAC.

- I had to modify the web.config, because the assembly Castle.ActiveRecord has to be explicitly added this way.

- You may notice I also added Castle.Monorail.Framework and Castle.Monorail.Views.Brail. Unfortunately it seems IIS is requiring those two files before reading all the web.config, so I add to change the property Copy Local to true (so in the /bin folder of the web application project you still can find these two assemblies and their related files). This means that you need to slightly the web application project as well to get those latest needed changes.

- I didn't test the web with a web site project configuration, if you happen to use such, you will probably get an error due missing assemblies. I do not advice to use such project option for an unending list of reasons, but if you really wish to persist on your own way, you should solve that just making sure the appropriate assemblies are copied in the /bin folder of the web application. Manually (I mean with some pre o post build event) copying files is a bad practice, because it will worsen the performances of the build, so exercise with care.

- You may be tempted to modify the list of the assemblies to include in the GAC. Please exercise care when doing so, because you can only add strongly typed assemblies this way. In example Newtonsoft.Json is not in that list because it is not a strongly typed assembly.

- Microsoft doesn't recommed to put in the GAC the assemblies you are developing. That is, MyDomainModel.dll or MyInitialisation.dll have still to be keept outside the GAC. If in the future you stabilize one of those libraries, you may decide to sign it (to make it strongly typed) and that to add it to the GAC.

I tested all of this on this Monorail based web project, but its results will probably be more or less similar with normal ASP.NET projects.

Batch file to cache assemblies

pause
"C:\Program Files\Microsoft.NET\SDK\v2.0 64bit\Bin\gacutil" /f /il .\toCache.txt
pause


Batch file to uncache assemblies

pause
"C:\Program Files\Microsoft.NET\SDK\v2.0 64bit\Bin\gacutil" /f /ul .\toUncache.txt
pause


ToCache.txt (list of file to cache, note the relative path to the working directory of the batch file)

..\..\Resources\bin\anrControls.Markdown.NET.dll
..\..\Resources\bin\Boo.Lang.Compiler.dll
..\..\Resources\bin\Boo.Lang.dll
..\..\Resources\bin\Boo.Lang.Parser.dll
..\..\Resources\bin\Castle.ActiveRecord.dll
..\..\Resources\bin\Castle.Components.Binder.dll
..\..\Resources\bin\Castle.Components.Common.EmailSender.dll
..\..\Resources\bin\Castle.Components.Common.EmailSender.SmtpEmailSender.dll
..\..\Resources\bin\Castle.Core.dll
..\..\Resources\bin\Castle.DynamicProxy.dll
..\..\Resources\bin\Castle.Monorail.ActiveRecordSupport.dll
..\..\Resources\bin\Castle.Monorail.Framework.dll
..\..\Resources\bin\Castle.Monorail.Views.Brail.dll
..\..\Resources\bin\Castle.Services.Logging.Log4netIntegration.dll
..\..\Resources\bin\Iesi.Collections.dll
..\..\Resources\bin\log4net.dll
..\..\Resources\bin\NHibernate.dll
..\..\Resources\bin\NHibernate.Caches.SysCache.dll
..\..\Resources\bin\NHibernate.Generics.dll
..\..\Resources\bin\Nullables.dll
..\..\Resources\bin\Nullables.NHibernate.dll


Batch file to uncache assemblies

anrControls.Markdown.NET
Boo.Lang.Compiler
Boo.Lang
Boo.Lang.Parser
Castle.ActiveRecord
Castle.Components.Binder
Castle.Components.Common.EmailSender
Castle.Components.Common.EmailSender.SmtpEmailSender
Castle.Core
Castle.DynamicProxy
Castle.Monorail.ActiveRecordSupport
Castle.Monorail.Framework
Castle.Monorail.Views.Brail
Castle.Services.Logging.Log4netIntegration
Iesi.Collections
log4net
NHibernate
NHibernate.Caches.SysCache
NHibernate.Generics
Nullables
Nullables.NHibernate