Just some ideas

Mar 4, 2010 at 8:34 AM

Hi

I' am just starting to prepare the migration of some mid- to large-size projects based on Unity 1.2 to Unity 2.0. I know Unity 2.0 is already in Beta phase and there will be not much chance to include new features or make big changes. However I list here some wishes, change requests and considerations. Perhaps they could be taken into account when defining the Unity 2.x Backlog …

  • The Unity and Object Builder assemblies have been merged into a single one. Why not just merge the *.Configuration assemblies into their corresponding assemblies.
  • The Unity Service Locator implementation is now in the Unity assembly. This requires a dependency to the Service Location assembly. Why not just put it in an own assembly like Microsoft.Practices.Unity.ServiceLocation or so. In our project we provide an own Service Locator interface and implementation with much more functionality and now we have to deploy another assembly (Microsoft.Practices.Unity.ServiceLocation) as it was before with the Object Builder assembly.
  • It would be great to have some more overloads on the Injection factory that allows specifying a delegate without the IUnityContainer or to register a type implementing a IFactory interface or something like that.
  • In Custom Object Builder Strategy we must know the Type that is requesting the creation of the actual Type that is being built (so we can create log4net ILogger instances using the type that has the dependency to the ILogger). We have achieved this by adding another Object Builder Strategy that uses a Policy to build a Stack of all types that are built during a single build up call. It would be nice if some sort of functionality would be supported directly by the Object Builder.
  • Finally it would be great to have built in some extension methods, like the following ones:
    • Overload to the IsRegistered method to query the container if a type is registered with a given LifetimeManager.
    • Some TryResolve methods return null instead of throwing an exception if the requested type could not be resolved.
    • An IsExtensionConfigured method to ask the container a given type of Unity Container Extension is configured.
    • A RegisterExtension method that resolves an Unity Container Extension trough the Unity Container and adds the created instance of the Unity Container Extension to the container.
    • An EnsureExtension method that returns a Unity Container Extension instance (like the Configure methods), but calls the above RegisterExtension method if it is not already registered.

It would be great if we see some of the above points in any future release of Unity. Keep up the really good work! We love it …

Thanks, Marco

Mar 5, 2010 at 4:53 AM
Edited Mar 5, 2010 at 4:55 AM

Thanks for the suggestions! You're right, it's pretty late in the game at this point, so I don't expect any of these to make it into 2.0. But I'd like to address your points one by one, because I think it's useful:

  • The Unity and Object Builder assemblies have been merged into a single one. Why not just merge the *.Configuration assemblies into their corresponding assemblies.

The reason that configuration is separate is because the core container is useful without the config file support, and there are people who can and will use the container without the configuration stuff. As such, we want to give the option. ObjectBuilder2 was originally separate because it was believed that there would be people using it separately from Unity. That turned out not to be the case, so we merged them to simplify deployment. If future customer feedback indicates it's not useful to not have to deploy config, we will merge it in.

  • The Unity Service Locator implementation is now in the Unity assembly. This requires a dependency to the Service Location assembly. Why not just put it in an own assembly like Microsoft.Practices.Unity.ServiceLocation or so. In our project we provide an own Service Locator interface and implementation with much more functionality and now we have to deploy another assembly (Microsoft.Practices.Unity.ServiceLocation) as it was before with the Object Builder assembly.

Actually, you only have to deploy Microsoft.Practices.ServiceLocation if you are actually using the adapter. If you're not using it, you don't need the assembly. When you add a reference Visual Studio will copy the ServiceLocation DLL automatically, but you don't actually need it. You only need to deploy that assembly if you're going to use it. Believe me, I very specifically tested this scenario - I made sure you didn't need the extra assembly unless it was adding value.

  • It would be great to have some more overloads on the Injection factory that allows specifying a delegate without the IUnityContainer or to register a type implementing a IFactory interface or something like that.

Certainly up for discussion in future versions. Until now, the only request I've gotten for was to get the type being resolved passed into the factory.

  • In Custom Object Builder Strategy we must know the Type that is requesting the creation of the actual Type that is being built (so we can create log4net ILogger instances using the type that has the dependency to the ILogger). We have achieved this by adding another Object Builder Strategy that uses a Policy to build a Stack of all types that are built during a single build up call. It would be nice if some sort of functionality would be supported directly by the Object Builder.

We could certainly add a stack of build keys or something along those lines, but I'd need more information about the scenario to make sure it's actually solving the problem. Would be good to discuss for future releases.

  • Finally it would be great to have built in some extension methods, like the following ones:
    • Overload to the IsRegistered method to query the container if a type is registered with a given LifetimeManager.
    • Some TryResolve methods return null instead of throwing an exception if the requested type could not be resolved.
    • An IsExtensionConfigured method to ask the container a given type of Unity Container Extension is configured.
    • A RegisterExtension method that resolves an Unity Container Extension trough the Unity Container and adds the created instance of the Unity Container Extension to the container.
    • An EnsureExtension method that returns a Unity Container Extension instance (like the Configure methods), but calls the above RegisterExtension method if it is not already registered.

All of those are easily achieved now. IsRegistered overloads won't happen from me; that's what container.Registrations + Linq is for:

container.Registrations.Where(r => r.RegisteredType == typeof(IWhatever) && r.LifetimeManagerType == typeof(MyLifetimeManager))

Or something to that effect (don't have the exact code in front of me).

IsExtensionConfigured is similarly easy:

 

container.Configure<MyExtensionType>() != null

 

And the other ideas are similarly simple to implement. I'm hesitant to start throwing in utility methods like this that are outside the direct mainstream use cases without strong community support, as they'd just clutter up the API otherwise.

Thanks for the suggestions, and keep them coming. There's some good ideas here that could very well make it into future versions!

-Chris

Mar 15, 2010 at 8:14 AM
Edited Mar 15, 2010 at 8:15 AM

Hi Chris

Thank you for your answer and excuse my late reply...

I have just once more some inputs to some of teh points:

  • *.Configuration assemblies:

In my opinon, even if the configuration stuff is merged into the Unity assembly I can choose whether i want to configure the container by code or from configuration. It is like in other .NET Framework parts such as WCF or so. But it's just a detail.

  • Microsoft.Practices.ServiceLocation assembly:

It really don't needs the assembly. I didn't realized that. So its perfect.

  • Stack of Build Key's in Object Builder:

A Stack of all build key's within the Object Builder Context is exactly what I need. I need it for the following scenario:

In log4net you have an ILogger interface. ILogger instances can be created using the LoggerManager class. For creating ILogger instances you can specifiy a Type. This Type is used as the source of the log entries being written with that ILogger instance.

So we created a Object Builder Strategy that whenever a ILogger is requested it is created using the LoggerManager. For correctly creating such a logger we need the Type that actualy is requesting the ILogger dependency. So we have created another Object Builder Strategy that tracks all Build Key's requested in a Stack and stores it in a custom transient policy. It currently works perfect for us. But if we would have built in some kind of build key stack directly in the Object Builders context, we wouldn't need our "Build Tracking Extension".

Thanks, and perhaps one of them could made it into Unity 2.x. Who knows ... ;-)

Marco

 

 

 

Mar 28, 2010 at 6:09 PM
marcoerni wrote:
  • Stack of Build Key's in Object Builder:

A Stack of all build key's within the Object Builder Context is exactly what I need. I need it for the following scenario:

In log4net you have an ILogger interface. ILogger instances can be created using the LoggerManager class. For creating ILogger instances you can specifiy a Type. This Type is used as the source of the log entries being written with that ILogger instance.

So we created a Object Builder Strategy that whenever a ILogger is requested it is created using the LoggerManager. For correctly creating such a logger we need the Type that actualy is requesting the ILogger dependency. So we have created another Object Builder Strategy that tracks all Build Key's requested in a Stack and stores it in a custom transient policy. It currently works perfect for us. But if we would have built in some kind of build key stack directly in the Object Builders context, we wouldn't need our "Build Tracking Extension".

 I would be interested in this as well.  I'm the Architect on a system that we currently have in an Alpha/Prototype state.  It is the classic Big Ball of Mud in WinForms.  For our Beta/Production product we actaully have the opportunity to rewrite the application from the ground up.  It is my intention to write it using WPF with the MVVM pattern (using the Cinch framework) and using Unity for IoC.  The plan is to use log4net for logging, as that is what we currently use, and I don't see a compelling reason to change.  I'm trying to figure out how to inject the ILog objects into our object but still maintain the richness in the log using the Type requesting the logger from the LoggerManager.  I welcome any solution to this.  I really don't want all those static instances of the logger in every class. 

As an aside, I'm somewhat interested in the Simple Logging Facade here on codeplex too that would allow me to get bread the dependencey on log4net in every project, but then I just replace one for another.  I then see how to abstract out SLF from your project as well.  At what point does the layers of abstraction become an Anti-pattern itself?  Regardless, while I really like some of the functionality of SLF, I think I'm going to just stick with calling log4net directly in my project.

Marco, have you considered adding your Build Tracking Extension as a contrib to Unity?

Scott

 

Apr 6, 2010 at 8:35 AM

Hi Scott
Unfortunately currently I haven’t really the time to put all them together in a Contrib project. But I have just copied the required class together and will post it here. Hope this helps.
The following code shows the ‘Build Tracking’ extension to track what Type is requesting a given dependency.

using System.Collections.Generic;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;

namespace UnityContrib {

    public class BuildTracking : UnityContainerExtension {

        protected override void Initialize() {
            this.Context.Strategies.AddNew<BuildTrackingStrategy>(UnityBuildStage.TypeMapping);
        }

        public static IBuildTrackingPolicy GetPolicy(IBuilderContext context) {
            return context.Policies.Get<IBuildTrackingPolicy>(context.BuildKey, true);
        }

        public static IBuildTrackingPolicy SetPolicy(IBuilderContext context) {
            IBuildTrackingPolicy policy = new BuildTrackingPolicy();
            context.Policies.SetDefault<IBuildTrackingPolicy>(policy);
            return policy;
        }
    }

    public class BuildTrackingStrategy : BuilderStrategy {

        public override void PreBuildUp(IBuilderContext context) {
            IBuildTrackingPolicy policy = BuildTracking.GetPolicy(context);
            if (policy == null) {
                policy = BuildTracking.SetPolicy(context);
            }

            policy.BuildKeys.Push(context.BuildKey);
        }

        public override void PostBuildUp(IBuilderContext context) {
            IBuildTrackingPolicy policy = BuildTracking.GetPolicy(context);
            if ((policy != null) && (policy.BuildKeys.Count > 0)) {
                policy.BuildKeys.Pop();
            }
        }
    }

    public interface IBuildTrackingPolicy : IBuilderPolicy {
        Stack<object> BuildKeys { get; }
    }

    public class BuildTrackingPolicy : IBuildTrackingPolicy {

        public BuildTrackingPolicy() {
            this.BuildKeys = new Stack<object>();
        }

        public Stack<object> BuildKeys { get; private set; }
    }
}

And this code shows how to use the ‘Build Tracking’ extension for creating log4net log objects. We are not using log4net log objects directly. We have them encapsulated in ITracer and ITracerFactory interfaces. But you also can use a library such as the Common Logging or so.

using System;
using System.Linq;
using log4net;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;

namespace UnityContrib {

    public class LogCreation : UnityContainerExtension {

        protected override void Initialize() {
            this.Context.Strategies.AddNew<LogCreationStrategy>(UnityBuildStage.PreCreation);
        }
    }
    public class LogCreationStrategy : BuilderStrategy {

        public bool IsPolicySet { get; private set; }

        public override void PreBuildUp(IBuilderContext context) {
            Type typeToBuild = BuildKey.GetType(context.BuildKey);
            if (typeof(ILog).Equals(typeToBuild)) {

                if (context.Policies.Get<IBuildPlanPolicy>(context.BuildKey) == null) {
                    Type typeForLog = LogCreationStrategy.GetLogType(context);
                    IBuildPlanPolicy policy = new LogBuildPlanPolicy(typeForLog);
                    context.Policies.Set<IBuildPlanPolicy>(policy, context.BuildKey);

                    this.IsPolicySet = true;
                }
            }
        }

        public override void PostBuildUp(IBuilderContext context) {
            if (this.IsPolicySet) {
                context.Policies.Clear<IBuildPlanPolicy>(context.BuildKey);
                this.IsPolicySet = false;
            }
        }

        private static Type GetLogType(IBuilderContext context) {

            Type logType = typeof(ILog);

            IBuildTrackingPolicy buildTrackingPolicy = BuildTracking.GetPolicy(context);
            if ((buildTrackingPolicy != null) && (buildTrackingPolicy.BuildKeys.Count >= 2)) {
                logType = BuildKey.GetType(buildTrackingPolicy.BuildKeys.ElementAt(1));
            }

            return logType;
        }
    }

    public class LogBuildPlanPolicy : IBuildPlanPolicy {

        public LogBuildPlanPolicy(Type logType) {
            this.LogType = logType;
        }

        public Type LogType { get; private set; }

        public void BuildUp(IBuilderContext context) {
            if (context.Existing == null) {
                ILog log = LogManager.GetLogger(this.LogType);
                context.Existing = log;
            }
        }
    }

}

So, feel free to use this code and hope something similar as the ‘Build Tracking’ extension will get into Unity 2.1 … ;)
Marco

Apr 6, 2010 at 9:01 AM

Thanks a lot Marco!  I'm sure myself and several others will make good use of the code you posted!

Apr 9, 2010 at 4:12 PM
Edited Apr 9, 2010 at 4:13 PM

Ok, a couple of tweaks I've made to Marco's code if anyone is interested.

First, the BuildKey static class no longer exists as of Beta 2, but a Type property has been added to the BuildKey propery hanging off of the context.

In the LogCreation class above change this line of PreBuild up

Type typeToBuild = BuildKey.GetType(context.BuildKey);

to

Type typeToBuild = context.BuildKey.Type;

 

In the GetLogType method of the same class, change this line:

logType = BuildKey.GetType(buildTrackingPolicy.BuildKeys.ElementAt(1));

to:

logType = ((NamedTypeBuildKey)buildTrackingPolicy.BuildKeys.ElementAt(1)).Type;

 

I've also added a tweak to walk up the stackframe to get propper logging identification when the ILog is not resolved as part of dependency injection, but directly through a container.Resolve<ILog>(); call:  I changed the GetLogType method to:

private static Type GetLogType(IBuilderContext context) {
	Type logType = typeof(ILog);
	IBuildTrackingPolicy buildTrackingPolicy = BuildTracking.GetPolicy(context);
	if ((buildTrackingPolicy != null) && (buildTrackingPolicy.BuildKeys.Count >= 2)) {
		logType = ((NamedTypeBuildKey)buildTrackingPolicy.BuildKeys.ElementAt(1)).Type;
	}
	else {
		StackTrace stackTrace = new StackTrace();
		//first two are in the log creation strategy, can skip over them
		for (int i = 2; i < stackTrace.FrameCount; i++) {
			StackFrame frame = stackTrace.GetFrame(i);
			logType = frame.GetMethod().DeclaringType;
			//Console.WriteLine(logType.FullName);
			if (!logType.FullName.StartsWith("Microsoft.Practices")) {
				break;
			}
		}
	}
	return logType;
}
I'm sure I can probably skip over more than just the first 2 frames, but I didn't want to get carried away, 
and that gets me into the Microsoft.Practices namespaces witches makes my condition easier. :)  
I do wonder if that can be made more efficient.
Mar 22, 2011 at 1:17 AM

Hi Marco and Scott,

Thank you very much for posting this code - I've seen lots questions asked about how to use log4net properly with Unity; up until now, I've had to put up with the standard use of an injection factory to create my instances, which then pick up the type of the class they were instantiated in, rather than where they are being used. Your code fixes all of that, and removes the need for me to explicitly register ILog.

For the sake of completeness, here's the code to actually use log4net with the above extensions.

public class Program
{
	public static void Main(string[] args)
	{
		UnityContainer container = new UnityContainer();
		container
			.AddNewExtension<BuildTracking>()
			.AddNewExtension<LogCreation>();

		// register your other types here

		ILog logger = container.Resolve<ILog>();
		var myCode = new MyClassWithLoggerDependency(logger);
		myCode.DoStuff();
	}
}

Any logging which occurs inside MyClassWithLoggerDependency will now log with the context of that class, rather than that of Program. As Marco says, hopefully something like this will make it into the next release of Unity.

Again, many thanks, guys!

May 11, 2012 at 2:22 AM
Edited May 11, 2012 at 2:56 AM

Hello fellow coders,

I found this solution to be exactly what I was looking for. I just wanted to take this solution one step further. The problem I saw was that the LogCreation implementation as presented is strictly tied to the log4Net framework. I have not used many other logging frameworks, but I would conjecture that I might want to switch to another framework at some point, I would like to on visit a very small amount and very focused piece of code the make the switch. So, I set out to adapt the LogCreation implementation to be generic for any Logging facility. Thus, I made a generic parameter for the Logger interface, and provided an interface to extract the creation of the logger from this implementation.

I'll just present the finished product, which includes the edits provided previously by ThunderEagle.

 

using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;

namespace UnityContrib
{
    /// 
    /// This class decouples the following classes from any specific logging framework.
    /// 
    public interface ILogFactory
    {
        /// 
        /// This method must return an instance of a Logger.
        /// 
        /// The type of the class that is using the logger.
        /// An instance of a logger.
        object GetLogger(Type parentType);
    }

    public class LogCreation<T,U> : UnityContainerExtension where U : ILogFactory, new() {

        protected override void Initialize() {
            this.Context.Strategies.AddNew>(UnityBuildStage.PreCreation);
        }
    }
    public class LogCreationStrategy<T,U> : BuilderStrategy where U : ILogFactory, new() 
    {

        public bool IsPolicySet { get; private set; }

        public override void PreBuildUp(IBuilderContext context) {
            Type typeToBuild = context.BuildKey.Type;
            if (typeof(T).Equals(typeToBuild)) {

                if (context.Policies.Get(context.BuildKey) == null) {
                    Type typeForLog = LogCreationStrategy.GetLogType(context);
                    ILogFactory factory = new U();
                    IBuildPlanPolicy policy = new LogBuildPlanPolicy(typeForLog, factory);
                    context.Policies.Set(policy, context.BuildKey);

                    this.IsPolicySet = true;
                }
            }
        }

        public override void PostBuildUp(IBuilderContext context) {
            if (this.IsPolicySet) {
                context.Policies.Clear(context.BuildKey);
                this.IsPolicySet = false;
            }
        }

        private static Type GetLogType(IBuilderContext context) {

            Type logType = typeof(T);

            IBuildTrackingPolicy buildTrackingPolicy = BuildTracking.GetPolicy(context);
            if ((buildTrackingPolicy != null) && (buildTrackingPolicy.BuildKeys.Count >= 2)) {
                logType = ((NamedTypeBuildKey)buildTrackingPolicy.BuildKeys.ElementAt(1)).Type;
            }
            else
            {
                StackTrace stackTrace = new StackTrace();
                //first two are in the log creation strategy, can skip over them
                for (int i = 2; i < stackTrace.FrameCount; i++)
                {
                    StackFrame frame = stackTrace.GetFrame(i);
                    logType = frame.GetMethod().DeclaringType;
                    //Console.WriteLine(logType.FullName);
                    if (!logType.FullName.StartsWith("Microsoft.Practices"))
                    {
                        break;
                    }
                }
            }

            return logType;
        }
    }

    public class LogBuildPlanPolicy : IBuildPlanPolicy {

        private ILogFactory logFactory;
        public LogBuildPlanPolicy(Type logType, ILogFactory factory) {
            this.LogType = logType;
            logFactory = factory;
        }

        public Type LogType { get; private set; }

        public void BuildUp(IBuilderContext context) {
            if (context.Existing == null) {
                context.Existing = logFactory.GetLogger(this.LogType);
            }
        }
    }

}

 

This would make the usage (from CtrlAltDel) look like this...

 

using log4net;

public class Program
{
       internal class Log4NetFactory : UnityContrib.ILogFactory
       {
                public object GetLogger(Type parentType)
                {
                        return LogManager.GetLogger(parentType);
                }
       }

	public static void Main(string[] args)
	{
		UnityContainer container = new UnityContainer();
		container
			.AddNewExtension<BuildTracking>()
			.AddNewExtension<LogCreation<ILog,Log4NetFactory>();

		// register your other types here

		ILog logger = container.Resolve<ILog>();
		var myCode = new MyClassWithLoggerDependency(logger);
		myCode.DoStuff();
	}
}

 

Thanks for your contributions.

Oct 11, 2012 at 9:23 AM
Edited Oct 11, 2012 at 10:14 AM

Won't your classes need to know about log4net to use its interface, which kind of defeat the point?

Maybe you should define your own common interface and create wrapper classes for the logging frameworks you want to support?

Oct 11, 2012 at 2:36 PM

CommonLogging already supports Enterprise Library (Version 3, 4, and 5), log4Net, and NLog.  It presents a log4Net type interface that works with all 3 frameworks and is extensible so you can create other providers.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Jul 11, 2013 at 1:40 PM
Very nice guys. I do have one issue though. How does the ILog instance pick up the config? I have it in the App.Config section buts its not picking it up.
Jul 11, 2013 at 2:51 PM
You need to configure common.logging as well as Enterprise Library. It should be loaded automatically:
  <configSections>
    <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true"/>
    <sectionGroup name="common">
      <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging"/>
    </sectionGroup>
  </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.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" fileName="errors.log" formatter=""/>
      <add name="Rolling Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.RollingFlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" fileName="rolling.log" formatter="" rollInterval="Midnight" maxArchivedFiles="7"/>
    </listeners>
    <formatters>
    </formatters>
    <categorySources>
      <add switchValue="All" name="General">
        <listeners>
          <add name="Rolling 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>

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Jul 11, 2013 at 3:07 PM
PhilMurray wrote:
Very nice guys. I do have one issue though. How does the ILog instance pick up the config? I have it in the App.Config section buts its not picking it up.
In my particular case, I explicitly configure it in my startup code.
//logging configuration
FileInfo logConfig = new FileInfo("log4net.config");
if(logConfig.Exists) {
    XmlConfigurator.Configure(logConfig);
}
Jul 11, 2013 at 3:13 PM
Thanks ThunderEagle.

I figured that one out.