Dependency injection with custom trace listener

Nov 1, 2009 at 4:28 PM

 

Hi folks,

I'm trying to implement a custom trace listener, which will use a Linq datacontext, but i've run into a bit of a problem, likely stemming from my lack of knowledge on this subject =]
Obviously i don't want to create a new datacontext for every logging action that occurs, so i have a constructor with an IDataContextProvider param, which Unity provides a concrete perwebrequest lifetime implementation of at runtime. But when i run the project i get the following error:

 

"The current build operation (build key Build Key[Microsoft.Practices.EnterpriseLibrary.Logging.LogWriter, null]) failed: The type 'X.Infrastructure.XDBTraceListener' specified for custom trace listener named 'Custom Trace Listener' does not a default constructor, which is required when no InitData is specified in the configuration. (Strategy type ConfiguredObjectStrategy, index 2)"

I know that you can provide an initializeData="something" attribute on the listener in the config file... but this is a string only right? Whereas i want to inject the DataContextProvider. Note that everything works fine when i have IDataContextProvider specified as a constructor param in other classes, Unity provides the right concrete class, and i'm off and running IOC styles.

Any suggestions welcome and i've included the pertinent code below.

 

MVC project
Global.asax :

 

<font size="2" color="#0000ff"><font size="2" color="#0000ff">

private

</font></font><font size="2" color="#0000ff">

 

</font>

static IUnityContainer _iocContainer;

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    InitializeIoC();
}

protected
virtual void InitializeIoC()
{
   
if (_iocContainer == null)
    {
        _iocContainer = IoCContainerBuilder.Build("App_Data/Configuration/Entlib.config", MyConfig.ActiveUnityContainer);
       
ControllerBuilder.Current.SetControllerFactory(typeof(UnityControllerFactory)); // controller factory provided via MvcContrib
    }
}

then IOCContainerBuilder.cs :

 

<font size="2" color="#0000ff"><font size="2" color="#0000ff">

public

</font></font><font size="2" color="#0000ff">

 

</font>

static class IoCContainerBuilder
{
   
public static IUnityContainer Build(string configPath, string unityContainerName)
    {
        va
r map = new ExeConfigurationFileMap{ ExeConfigFilename = AppDomain.CurrentDomain.BaseDirectory + configPath};
       
var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
        
var section = config.GetSection("unity") as UnityConfigurationSection;
        
IUnityContainer container = new UnityContainer();
        section.Containers[unityContainerName].Configure(container);
       
container.RegisterType<IConnectionStringProvider, ConnectionStringProvider>(new ContainerControlledLifetimeManager());
        container.RegisterType<IDataContextProvider, DataContextProvider>(new UnityPerWebRequestLifetimeManager());
        return container;
    }
}

 

Entlib.config:

<
loggingConfiguration name="Logging Application Block" tracingEnabled="true" defaultCategory="General" logWarningsWhenNoCategoriesMatch="true">
    <
listeners>
        <
add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" traceOutputOptions="None" filter="All" type="X.Infrastructure.XDBTraceListener, SC2009.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Custom Trace Listener" formatter="Text Formatter" />
    </listeners>
    formatters... categorys... specialsources...
 etc
<loggingConfiguration>

 

 

 

<font size="2" color="#0000ff"><font size="2" color="#0000ff">

<

</font></font><font size="2" color="#0000ff">

 

</font>

unity>
     <
typeAliases>
        <
typeAlias alias="String" type="System.String, mscorlib" />
        <
typeAlias alias="IConnectionStringProvider" type="X.DataAccess.IConnectionStringProvider, X.DataAccess" />
        <
typeAlias alias="ConnectionStringProvider" type="X.DataAccess.ConnectionStringProvider, X.DataAccess" />
     </
typeAliases>
     <
containers>
        <
container name="live">
            <
types>
                <
type type="IConnectionStringProvider" mapTo="ConnectionStringProvider">
                    <
typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
                        <
constructor>
                            <
param name="connectionString" parameterType="String">
                                <
value value="Data Source=.\SQLEXPRESS;Initial Catalog=X;Integrated Security=True" />
                            </param>
                        </
constructor>
                     </
typeConfig>
                  </
type>
               </
types>
           </
container>
        </
containers>
    </
unity>

the DataContext and ConnectionString providers are pretty simple and vanilla so i'll leave taht out... and get to the relevant bit...

X.Infrastructure project
XDBTraceListener.cs :

 

<font size="2">

[

</font>

ConfigurationElementType(typeof(CustomTraceListenerData))]
public class XDBTraceListener : CustomTraceListener
{
    
public XDBTraceListener(IDataContextProvider dataContextProvider)
    {
       
db = dataContextProvider.DataContext;
    }

    
readonly XDataContext db;

   
public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
    {
       
if (data is LogEntry)
        {
            
try
           
{
                do stuff !
            } 
            catch
            {
                 more stuff
            }
         }
    }

    -- other methods --
}

 

Nov 1, 2009 at 8:03 PM

my first thought is that the UnityControllerFactory ( assuming that is initiating the unity call ) is using the default container and not the "live" named container you have setup.  Therefore the IDataContextProvider interface is not wired up and as the XDBTraceListener has no default ctor Unity cannot use the ctor provided.

I rolled by own UnityControllerFactory for an MVC project and didn't use the UnityContrib version so I'm not sure if you can use a named container ?

Cheers...

Robert

Nov 2, 2009 at 1:44 PM

There is no default container.. there is only the 'live' one. Besides if this was the case none of my IOCContainer managed classes would be being provided... but like i said i have other service classes which have various Interfaces in their constructors which all work perfectly. I have a bunch of "ISomethingService" classes which all depend on the IDataContextProvider to be DI into their constructors. All of these work fine.. it's just the TraceListener which is being a pain.

Nov 2, 2009 at 5:49 PM

I'll have to dig into the code, but my first guess would be that you're using the CustomerTraceListenerData to provide your configuration. The Entlib 4.1 unity integration is a little hacked together, and we actually rely on the config system to create the object. Thus you're getting the error. Can you try providing a specific configuration element for your custom trace listener?

 

Nov 3, 2009 at 11:58 AM

Just to clarify... are you saying that because i have:

<listeners>
        <
add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

... with the CustomTraceListenerData as the listenerDataType, that my Custom Trace Listener always looks to the Entlib.config file for it's IOC information rather than referring to the IOC Container created in Global.asax?

Also, could you please explain the "Can you try providing a specific configuration element for your custom trace listener"... i'm not too good with config of IOC via XML docs =]

Thanks for your time.