Development With A Dot

Blog on development in general, and specifically on .NET

Sponsors

News

My Friends

My Links

Permanent Posts

Portuguese Communities

March 2011 - Posts

NHibernate Interceptor for Dynamic Proxy Generation

NHibernate comes along with a nice code generation API that is used, for example, for lazy-loading proxy generation. It can, however, be used for other purposes, such as adding interfaces to the loaded entities, for example, INotifyPropertyChanged coming to my mind. Ayende and José have talked about this in the past, but I decided to publish my own updated version.

It has two parts: a custom NHibernate interceptor class that can be added to Configuration or session creation and a custom Castle DynamicProxy interceptor that is created from the former for each new entity loaded from the DB.

Here's the skeleton. You just have to fill in the blanks to add your own behavior.


public class CustomInterceptor : NHibernate.EmptyInterceptor
{
	class _CustomInterceptor : Castle.DynamicProxy.IInterceptor
	{
		private Boolean finishedLoading = false;

		void Castle.DynamicProxy.IInterceptor.Intercept(IInvocation invocation)
		{
			Object target = (invocation.InvocationTarget == null) ? invocation.Proxy : invocation.InvocationTarget;

			//check if the entity has finished loading
			if ((invocation.InvocationTarget == null) && (this.finishedLoading == false))
			{
				this.finishedLoading = true;
			}

			//TODO: do something before base method call
			if (invocation.Method.Name.StartsWith("get_") == true)
			{
				//getter invocation
			}
			else if (invocation.Method.Name.StartsWith("set_") == true)
			{
				//setter invocation
			}
			else
			{
				//method invocation
			}

			if (invocation.InvocationTarget != null)
			{
				//proceed with base implementation (base getter, setter or method call)
				invocation.Proceed();
			}

			//TODO: do something after base method call
			if (invocation.Method.Name.StartsWith("get_") == true)
			{
				//getter invocation
			}
			else if (invocation.Method.Name.StartsWith("set_") == true)
			{
				//setter invocation
			}
			else
			{
				//method invocation
			}
		}
	}

	private static readonly ProxyGenerator proxyGenerator = new ProxyGenerator();
	private ISession session = null;

	public static Object CreateProxy(Type type)
	{
		List<Type> interfaces = new List<Type>();
		//TODO: add interfaces to list

		Object instance = null;

		if ((interfaces.Count != 0) && (type.IsSealed == false))
		{
			//TODO: pass any custom parameters to the _CustomInterceptor class
			instance = proxyGenerator.CreateClassProxy(type, interfaces.ToArray(), new _CustomInterceptor());
		}
		else
		{
			instance = Activator.CreateInstance(type);
		}

		return(instance);
	}

	public static T CreateProxy<T>() where T: class, new()
	{
		Type type = typeof(T);
		return(CreateProxy(type) as T);
	}

	public override String GetEntityName(Object entity)
	{
		if (entity.GetType().Assembly.FullName.StartsWith("DynamicProxyGenAssembly2") == true)
		{
			return (entity.GetType().BaseType.FullName);
		}
		else
		{
			return (entity.GetType().FullName);
		}
	}

	public override void SetSession(ISession session)
	{
		this.session = session;
		base.SetSession(session);
	}

	public override Object Instantiate(String clazz, EntityMode entityMode, Object id)
	{
		if (entityMode == EntityMode.Poco)
		{
			Type type = Type.GetType(clazz, false);

			if (type != null)
			{
				Object instance = CreateProxy(type);

				this.session.SessionFactory.GetClassMetadata(clazz).SetIdentifier(instance, id, entityMode);

				return (instance);
			}
		}

		return (base.Instantiate(clazz, entityMode, id));
	}
}

You can add the interceptor at two levels:

  • The Configuration object: all sessions will inherit this interceptor
  • The ISession object

Here's an example for each:


Configuration cfg = ...;
cfg.SetInterceptor(new CustomInterceptor());

ISessionFactory factory = cfg.BuildSessionFactory();
ISession session = factory.OpenSession(new CustomInterceptor());

As you can see, the outer _CustomInterceptor class is the one that NHibernate must know. You should add any interfaces that the proxy should implement. The inner _CustomInterceptor class becomes part of the generated proxy, and the Intercept method will be called for each interceptable (virtual or new) property and method. If you want, you can add code before and after the base call.

I have my own implementation which adds some common interfaces used for data binding, editing and validation: example, INotifyPropertyChanged and example, INotifyPropertyChanging, example, IEditableObject and example, IDataErrorInfo (with the help of NHibernate Validator). Feel free to roll your own!

Bookmark and Share
Calling WCF Web Services from JavaScript

This post was long due, so here it is. Prepare for a long post!

Whenever you need to consume a WCF web service from a web page, you have (at least) three options:

  • Have the ASP.NET ScriptManager generate a strongly-typed JavaScript proxy to the service that you can call directly (you even get Visual Studio intellisense!)
  • Use your own JavaScript, or some third party, library such as jQuery (which I use in my example) to invoke a service in REST style
  • Use your own JavaScript to invoke a service using SOAP

The first two require that you have control over the bindings specified in the Web.config file or, at least, the factory in the .svc file.

We want to be able to invoke a service looking like this:


public class Response
{
	public String A { get; set; }
	public String B { get; set; }
}

public Response PostTest(String a, String b);
public Response GetTest(String a, String b);

You probably know the difference between SOAP and REST, if not, check out this and this.

Let's start from the first option.

.NET 3.5 included a handy behavior, enableWebScript, which allows the ScriptManager to generate a proxy from the metadata published from the service. The service must look like this:


namespace WcfAjax.Services
{
	[DataContract]
	public class Response
	{
		[DataMember]
		public String A { get; set; }

		[DataMember]
		public String B { get; set; }
	}

	[ServiceContract(Namespace = "WcfAjaxServices")]
	[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
	public class TestService
	{
		[OperationContract]
		[WebInvoke]
		public Response PostTest(String a, String b)
		{
			return (new Response() { A = a, B = b });
		}

		[OperationContract]
		[WebGet]
		public Response GetTest(String a, String b)
		{
			return (new Response() { A = a, B = b });
		}
	}
}

Please note the following:

WebGetand WebInvoke allow us to specify wether we want to call the method using the HTTP GET method or the POST, respectively. This may be relevant if you want to send sensitive information, such as passwords, in which case you should use the POST method (the default if no attribute is specified); on the other hand, only GET requests can be cached.
By now you should probably know what the AspNetCompatibilityRequirements is, if not, go read the documentation.
As for the namespace, make sure you do not use something that looks like a URL, make it simple, you will see why.

You now need to register the service with the ScriptManager on the page (or master page) that you want to call the service in. Here's how we do it:


<asp:ScriptManager runat="server">
	<Services>
		<asp:ServiceReference Path="~/Services/TestService.svc"/>
	</Services>
</asp:ScriptManager>

Make sure the WCF bindings are configured this way in Web.config:


<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="WcfAjax.Services.TestService">
          <enableWebScript />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="WcfAjax.Services.TestService">
        <endpoint address="" behaviorConfiguration="WcfAjax.Services.TestService" binding="webHttpBinding" contract="WcfAjax.Services.TestService" />
      </service>
    </services>
</system.serviceModel>

The .svc file contains:


<%@ ServiceHost Language="C#" Debug="true" Service="WcfAjax.Services.TestService" CodeBehind="TestService.svc.cs" %>

Technically, you can go without using enableWebScript, if you specify a special factory in the .svc file:


<%@ ServiceHost Language="C#" Debug="true" Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" Service="WcfAjax.Services.TestService" CodeBehind="TestService.svc.cs" %>

And that's it. You can now call the service in JavaScript:


<script type="text/javascript">
	//<![CDATA[
	function test()
	{
		//the class name is named from the namespace specified in the OperationContract attribute
		//plus the contract class name
		var svc = new WcfAjaxServices.TestService();

		//the methods look like the ones defined in the contract, but they take 3 additional arguments:
		//- a function to call in case of success
		//- a function to call in case of error
		//- an optional context
		//result and error are JavaScript objects
		//methodName is the name of the function that started the request
		svc.GetTest('a', 'b', function(result, context, functionName)
		{
			window.alert('A: ' + result.A);
		}, function (error, context, methodName)
		{
			window.alert('error: ' + error);
		}, null);

		svc.PostTest('a', 'b', function(result, context, functionName)
		{
			window.alert('A: ' + result.A);
		}, function (error, context, methodName)
		{
			window.alert('error: ' + error);
		}, null);

	}
	//]]>
</script>

Moving on, the next option gives us total control over the way our parameters are sent to the service. Unfortunately, we cannot rely on automatically generated proxies, but it's easy anyway. This one relies on the webHttp behavior, which is also new on .NET 3.5. Specifically, this behavior allows REST-style calls.

Have your code look like this:


namespace WcfAjax.Services
{
	[ServiceContract(Namespace = "WcfAjaxServices")]
	[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
	public class RestTestService
	{
		[OperationContract]
		[WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
		public Response PostTest(String a, String b)
		{
			return (new Response() { A = a, B = b });
		}

		[OperationContract]
		[WebGet(UriTemplate = "GetTest?a={a}&b={b}", ResponseFormat = WebMessageFormat.Json)]
		public Response GetTest(String a, String b)
		{
			return (new Response() { A = a, B = b });
		}
	}
}

The configuration should look like this:


<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="WcfAjax.Services.RestTestService">
          <webHttp />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="WcfAjax.Services.RestTestService">
        <endpoint address="" behaviorConfiguration="WcfAjax.Services.RestTestService" binding="webHttpBinding" contract="WcfAjax.Services.TestService" />
      </service>
    </services>
</system.serviceModel>

As for the .svc file, nothing new:


<%@ ServiceHost Language="C#" Debug="true" Service="WcfAjax.Services.RestTestService" CodeBehind="RestTestService.svc.cs" %>

Now, the difference is in the way we invoke the service. Here's an example using jQuery AJAX:


<script type="text/javascript">
	//<![CDATA[
	function restGetTest()
	{
		$.ajax
		(
			{
				type: 'GET',
				url: 'Services/RestTestService.svc/GetTest',
				dataType: 'json',
				data: 'a=a&b=b',
				success: function (response, type, xhr)
				{
					window.alert('A: ' + response.A);
				},
				error: function (xhr)
				{
					window.alert('error: ' + xhr.statusText);
				}
			}
		);

		$.ajax
		(
			{
				type: 'POST',
				url: 'Services/RestTestService.svc/PostTest',
				dataType: 'json',
				contentType: 'application/json',
				data: '{ "a": "a", "b": "b" }',
				success: function (response, type, xhr)
				{
					window.alert('A: ' + response.PostTestResult.A);
				},
				error: function (xhr)
				{
					window.alert('error: ' + xhr.statusText);
				}
			}
		);
	}
	//]]>
</script>

Of note:

  • The data for the GET version must match the format specified in the WebGet attribute, by the UriTemplate property
  • For the POST version, the data object is a JSON-formatted string

Finally, some times we do not have possibility to change either the bindings or the factory that is used to create the services, and we have to deal with plain SOAP. Luckily, although SOAP can get quite complex, for simple scenarios it isn't too hard to handle.

The code:


namespace WcfAjax.Services
{
	[ServiceContract(Namespace = "WcfAjaxServices")]
	[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
	public class SoapTestService
	{
		[OperationContract]
		public Response PostTest(String a, String b)
		{
			return (new Response() { A = a, B = b });
		}

		[OperationContract]
		public Response GetTest(String a, String b)
		{
			return (new Response() { A = a, B = b });
		}
	}
}

Note this looks exactly like an ordinary WCF service, that doesn't even need ASP.NET compatibility mode. The configuration:


  <system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="WcfAjax.Services.SoapTestService"/>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    <services>
      <service name="WcfAjax.Services.SoapTestService">
        <endpoint address="" behaviorConfiguration="WcfAjax.Services.SoapTestService" binding="basicHttpBinding" contract="WcfAjax.Services.SoapTestService" />
      </service>
    </services>
  </system.serviceModel>

Unlike the other examples, we must use the basicHttpBinding, not , for this one.

The .svc file:


<%@ ServiceHost Language="C#" Debug="true" Service="WcfAjax.Services.SoapTestService" CodeBehind="SoapTestService.svc.cs" %>

Finally, the invocation (SOAP only allows POST):


<script type="text/javascript">
	//<![CDATA[
	function test()
	{
		$.ajax
		(
			{
				type: 'POST',
				url: 'Services/SoapTestService.svc',
				dataType: 'xml',
				contentType: 'text/xml',
				data: '' +
				//uncomment this if you want to send a custom SOAP header
				//'' +
				//'' +
				//'MyName' +
				//'MyPassword' +
				//'' +
				//''
				'' +
				'' +
				'a' +
				'b' +
				'' +
				'' +
				'',
				beforeSend: function (xhr)
				{
					xhr.setRequestHeader('SOAPAction', 'WcfAjaxServices/SoapTestService/PostTest');
				},
				success: function (response, type, xhr)
				{
					var a = '';
					if (response.childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[0].textContent)
					{
						//Chrome and Firefox
						a = response.childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[0].textContent;
					}
					else if (response.childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[0].text)
					{
						//IE
						a = response.childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[0].text;
					}
					window.alert('A: ' + a);
				},
				error: function (xhr)
				{
					window.alert('error: ' + xhr.statusText);
				}
			}
		);
	}
	//]]>
</script>


And here you have it. Hope this turns out useful to anyone! If you need the code, drop me a line.

Bookmark and Share
New Location for .NET 4 GAC

.NET 4 newcomers may have realised that the old GAC location (%WINDIR%\Assembly) does not contain .NET 4 global assembly cache assemblies. Indeed, they have moved to %WINDIR%\Microsoft.NET\Assembly. It is worth noting that this folder does not use the shell extension that the older one uses, which prevents us from directly looking at the folder's contents, which, IMO, is nice (I mean, the new behavior). The old folder continues to host pre-.NET 4 assemblies.

Bookmark and Share
Posted: Mar 16 2011, 11:02 AM by Ricardo Peres | with 1 comment(s)
Filed under: ,
NHibernate 3.1.0 Released

Source and binares here, release notes here.

Bookmark and Share
Web Developers Can Feel Like Kids in a Candy Store

A very interesting read about the latest Microsoft technologies from Vishal Joshi: Web Developers can feel like Kids in a Candy Store.

Bookmark and Share
Local Entities with NHibernate

You may know that Entity Framework Code First has a nice property called Local which lets you iterate through all the entities loaded by the current context (first level cache). This comes handy at times, so I decided to check if it would be difficult to have it on NHibernate. It turned out it is not, so here it is! Another nice addition to an NHibernate toolbox!


	public static class SessionExtensions
	{
		public static IEnumerable<T> Local<T>(this ISession session)
		{
			ISessionImplementor impl = session.GetSessionImplementation();
			IPersistenceContext pc = impl.PersistenceContext;

			foreach (Object key in pc.EntityEntries.Keys)
			{
				if (key is T)
				{
					yield return ((T) key);
				}
			}
		}
	}
	
	//simple usage
	IEnumerable<Post> localPosts = session.Local<Post>();

Bookmark and Share
More Posts