Tuesday, May 20, 2014

MS CRM Simulating OnChange Javascript event for Owner field

You might get a surprise when you add the On Change event to an owner field - you will find that it doesn't fire.  The reason for this is that the when changing the owner of a record the form is actually saved.

Lucky for you, there is a way to achieve the same functionality as the On Change, and it's quite straight forward.

Save Mode = Assign

Basically, you can add a On Save event (making sure you tick the pass the context box when you set it up), and then you can find out what triggered the On Save event inspecting the SaveMode and finding out if it was triggered by an "Assign".

OwnerId Is Dirty Check

The only other thing you'll need to do is check also if the OwnerId field is dirty.  Why is this needed?  Well, imagine that you've got a required field that you haven't filled in and then you go to change the owner. As changing the Owner causes an On Save event, this also triggers validation and you'll get an error message indicating the field is required before it gets to your logic, and the Save won't actually occur.  This leaves the form in state where the OwnerId has actually been changed but your "OnChange" logic won't fire on the next save because the logic is looking for an Save Mode of "Assign" - which won't be raised again until someone attempts to re-assign the record.  So, the next best thing is to do a check to see if the OwnerId is dirty (which will only happen if the Save attempt failed validation when the owner is changed) and your on change logic can fire then.  Not perfect, but good enough in most situations.

So here's an example of some code that will do the job:

function Form_OnSave(context) {

    var saveMode_Assign = 47;

    if (context.getEventArgs().getSaveMode() == saveMode_Assign 
        || Obs.getAttribute("ownerid").getIsDirty()) {
        OwnerId_OnChange(); // Put your logic here!

Saturday, March 22, 2014

S#arp Architecure, Multiple Database and Class Maps

Recently, I've been updated a ASP.NET MVC project which is based on the S#arp Architecture.  I needed to extend the existing function to talk to a second database.

In order to do this, I followed the S#arp Architecture guide to multiple databases which can be found here. This shows you how to add a second NHibernate configuration to your project and how to apply an attribute to your repository so it knows that which NHibernate configuration to use. This seemed to be perfect as out of the box, NHibernate didn't seem to have the facility to support multiple database sessions - well at least not easily.

So following the guide, I integrated the changes into the existing software - unfortunately this didn't work.

The issue was that I had the POCO classes and NHibernate Class Maps for both databases in the one assembly NHibernate would attempt to apply the class maps and POCO classes to only one database Session - causing NHibernate to throw "No Persister Found" errors and similar errors. As I didn't like the idea of splitting the project up into two assemblies for the sake of it (one for each database), I went looking for alternatives.

To fix the POCO issue was easy,  I just created an empty interface representing each database and applied that interface to each of the POCO objects accordingly, then in AutoPersistenceModelGenerator I simply had two different versions of the Generate method for each database which I used when settings up my Sessions in the calls to NHibernateSession.Init() and NHibernateSession.AddConfiguration().

The Class maps were not so easy, after doing a lot of research and finding that ClassMaps can't be filtered out in the Same way as POCO objects, I was very much stuck until I found this blog post.  Although this blog post had nothing to do with multiple databases, it did point out that in the S#arp Architecture you should not use ClassMaps, but instead use Overrides that are provided by the S#arp Architecture.

In the S#arp Architecture when using AutoMapping, Overrides represent those classes which shouldn't be AutoMapped on conventions and instead have custom mappings that don't match the convention.  The Overrides are basically the same ClassMaps and also use the same syntax.

To my surprise after converting the ClassMaps to Overrides (which only took a few minutes), it seems that S#arp Architecture could then work out which Override (i.e. Map) applied to which database and applied the where they were appropriate - no more errors.  And because there were no long any Class Maps, NHibernate wasn't trying to automatically assume that all ClassMaps belonged to the current Session that was being initialized.

Anyhow, thought I would document this in case someone else is facing the same situation.