Resolve "Policy enabled" objects with Unity2

Jun 11, 2010 at 8:08 AM
Edited Jun 11, 2010 at 8:12 AM

Hello,

I want to mix configuration and code in a way that I cannot find the solution for.  I'm using Unity2.0 and Enterprise Library 5.

I want to have the registration and resolving of my objects in code, but in a way that later on can add policies in the config file, so that I can change the behavior (Logging, Tracing etc) with the configuration, without having to recompile.

The shorter description is; I want to resolve objects so that they are "Policy enabled", so that I later on can add policies in configuration that will be picked up by the resolved objects.

Here is my stab at it.... It resolves alright but the logging (via polices) doesn't happen. What is wrong?

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
static class Module1
{


	private static IUnityContainer _container = new UnityContainer();
	public static void Main()
	{
		ConfigureUnity();

		object m = _container.Resolve<IMarcus>();

		m.CallMe();
		Console.WriteLine(m.CallMeAgain());
		Console.ReadLine();
	}

	/// <summary>
	/// I want to be able to add/update/change policies, matching rules and callhandlers
	/// in the configuration file without being forced to recompile
	/// 
	/// So the following scenarios can be accomplished:
	///    - add a performance counter call handler on a server to find problems
	///    - increase the level of logging information to find problems
	///    - etc.
	/// </summary>
	/// <remarks></remarks>
	private static void ConfigureUnity()
	{
		_container.AddNewExtension<Interception>();

		_container.RegisterType<IMarcus, Marcus>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>());
	}


	public class Marcus : IMarcus
	{

		public void CallMe()
		{
		}
		public string CallMeAgain()
		{
			return "CallMeAgain called";
		}
	}

	public interface IMarcus
	{
		void CallMe();
		string CallMeAgain();
	}

}

And here is the configuration:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<configSections>
		<section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null" requirePermission="true" />
		<section name="policyInjection" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration.PolicyInjectionSettings, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null" requirePermission="true" />
	</configSections>
	<loggingConfiguration name="" tracingEnabled="true" defaultCategory="General">
		<listeners>
			<add name="Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null"
                listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null"
                fileName="trace.log" formatter="Text Formatter" traceOutputOptions="DateTime, Timestamp, ProcessId" />
		</listeners>
		<formatters>
			<add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null"
                template="Timestamp: {timestamp}{newline}&#xA;Message: {message}{newline}&#xA;Category: {category}{newline}&#xA;Priority: {priority}{newline}&#xA;EventId: {eventid}{newline}&#xA;Severity: {severity}{newline}&#xA;Title:{title}{newline}&#xA;Machine: {localMachine}{newline}&#xA;App Domain: {localAppDomain}{newline}&#xA;ProcessId: {localProcessId}{newline}&#xA;Process Name: {localProcessName}{newline}&#xA;Thread Name: {threadName}{newline}&#xA;Win32 ThreadId:{win32ThreadId}{newline}&#xA;Extended Properties: {dictionary({key} - {value}{newline})}"
                name="Text Formatter" />
		</formatters>
		<categorySources>
			<add switchValue="All" name="General">
				<listeners>
					<add name="Flat File Trace Listener" />
				</listeners>
			</add>
		</categorySources>
		<specialSources>
			<allEvents switchValue="All" name="All Events" />
			<notProcessed switchValue="All" name="Unprocessed Category" />
			<errors switchValue="All" name="Logging Errors &amp; Warnings">
				<listeners>
					<add name="Flat File Trace Listener" />
				</listeners>
			</errors>
		</specialSources>
	</loggingConfiguration>

	<policyInjection>
		<policies>
			<add name="DemoPolicy">
				<matchingRules>
					<add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.TypeMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null"
                        name="InterfaceMatchingRule">
						<matches>
							<add match="IMarcus" />
						</matches>
					</add>
				</matchingRules>
				<handlers>
					<add type="Microsoft.Practices.EnterpriseLibrary.Logging.PolicyInjection.LogCallHandler, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null"
                        name="LoggingCallHandler">
						<categories>
							<add name="General" />
						</categories>
					</add>
				</handlers>
			</add>
		</policies>
	</policyInjection>
</configuration>

 

Jun 11, 2010 at 10:11 AM
Edited Jun 11, 2010 at 10:36 AM

Try adding the EnterpriseLibraryCoreExtension to your container. 

Your posted code also resolves an IMarcus interface and assigning it to an object.  I assume that's just a typo since it will generate a compile error on your end if it isn't an IMarcus.

Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com

Jun 11, 2010 at 11:13 AM
Edited Jun 11, 2010 at 11:25 AM

Hello, that looks very promising...

The resolving process was halted since I was missing a reference to Microsoft.Practices.EnterpriseLibarary.Logging in my project. Great!

That means that it's picking up my configuration.

But... when that reference was added (and the reference to Microsoft.Practices.EnterpriseLibrary.Common where EnterpriseLibraryCoreExtension is located)
it still doesn't work. Deep from the bellows of EntLib (in Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity.ReaderWriterLockExtension,
in the inlined class: ReaderWriterLockCompensator in the Recover-method) I get an exception "The read lock is being released without being held."

Am I missing something?

Jun 11, 2010 at 11:18 AM
Edited Jun 11, 2010 at 11:26 AM

Ahhh - saw the note here (http://msdn.microsoft.com/en-us/library/ff650299.aspx) just now...
Add the EnterpriseLibraryCoreExtension before any other extensions. I'll try that

Jun 11, 2010 at 11:35 AM
Edited Jun 11, 2010 at 11:47 AM

YES!

That did the trick!

Although I had some problems with an exception concering my resolved proxy "is attempting to implement an inaccessible interface."?
But that had to do with me writing the interface inside the Module1-class above. When I moved the interface and it's implementer to a class of it's own everything worked.

Thank you Sarah!

Here is my complete example (the .config-file has not changed from above):

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity;

static class Module1
{


	private static IUnityContainer _container = new UnityContainer();
	public static void Main()
	{
		ConfigureUnity();

		object m = _container.Resolve<IMarcus>();

		m.CallMe();
		Console.WriteLine(m.CallMeAgain());
		Console.ReadLine();
	}

	/// <summary>
	/// I want to be able to add/update/change policies, matching rules and callhandlers
	/// in the configuration file without being forced to recompile
	/// 
	/// So the following scenarios can be accomplished:
	///    - add a performance counter call handler on a server to find problems
	///    - increase the level of logging information to find problems
	///    - etc.
	/// </summary>
	/// <remarks></remarks>
	private static void ConfigureUnity()
	{
		_container.AddNewExtension<EnterpriseLibraryCoreExtension>();
		_container.AddNewExtension<Interception>();

		_container.RegisterType<IMarcus, Marcus>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>());
	}

}

public interface IMarcus
{
	void CallMe();
	string CallMeAgain();
}

public class Marcus : IMarcus
{

	public void CallMe()
	{
	}
	public string CallMeAgain()
	{
		return "CallMeAgain called";
	}
}