CustomExceptionHandler and Unity configuration

Oct 5, 2010 at 2:23 PM

Can anyone please tell me how to "configure" the constructor in Unity (EntLib 5/Unity 2.0) so that I can inject a custom logger?

the idea is this:

ctor:

public CustomHandler(NameValueCollection attribute, ILogStuff logger)

{

   this.logger=logger;

}

in the config i'd register the CustomHandler like so:

<register type="CustomHandler" name="ExceptionHandler">
        <constructor>

          <param value="NameValueCollection" name="attribute"></param>
          <param value="ILogStuff" name="logger"></param>
        
        </constructor>
      </register>

the maroon parameter being questionable.

I would of course also register the ILogStuff itfc with the container.

No matter what I try, it's always the single parameter ctor of my CustomHandler class that gets called...any ideas?

Thanks in advance

Oct 5, 2010 at 6:32 PM
Edited Oct 5, 2010 at 6:45 PM

Is this an actual custom exception handler for enterprise library? And if so, are you using [ConfigurationElementType(typeof(CustomHandlerData))] for your configuration element?

If that is the case, it doesn't matter what you specify in the Unity configuration, the Entlib configuration takes precedence, and the CustomerHandlerData will only ever call the single argument NameValueCollection constructor. If you want to call another constructor, you'll need to write a custom configuration element type (pretty simple) to tell Entlib which constructor you want to use. This process is explained in the Entlib documentation. Also, I provided another example of this on StackOverflow recently

If that's not what you're doing, then please provide more detail about what you're trying to do, as I'm obviously not understanding your problem here.

 

Nov 3, 2010 at 5:16 PM
Edited Nov 3, 2010 at 5:23 PM

Thanks for your reply. I looked at the documentation as well as the example on stackoverflow. Together, they got through the exception minefield and the correct constructor is being called BUT the arguments are always null.

Assuming that I'm not required to use ConfigurationProperty/Reference attributes, and that the registration of my ExceptionHandler is correct, I can't see immediately what else I can do to ensure that my arguments are instantiated.


    [ConfigurationElementType(typeof(MyExceptionHandlerData))]
    public class MyExceptionHandler : IExceptionHandler
    {
        ILogStuff _logger;
        public MyExceptionHandler(ILogger logger, NameValueCollection attribute)
        { this._logger = logger; }
        public MyExceptionHandler(NameValueCollection attribute){ }

        Exception IExceptionHandler.HandleException(Exception exception, Guid handlingInstanceId)
        {
            _logger.LogSomethingSomewhere("Hello World");
            return exception;
        }
    }

 

    public class MyExceptionHandlerData : ExceptionHandlerData
    {
        ILogStuff logStuff;
        public ILogStuffLogger { get { return logStuff; } set {logStuff = value; } }
        NameValueCollection nameValueCollection;
        public NameValueCollection Attribute { get { return nameValueCollection; } set { nameValueCollection = value; } }

        public MyExceptionHandlerData() : base(typeof(MyExceptionHandler)) { }
        public MyExceptionHandlerData(ILogStuff logger, NameValueCollection attribute) : base(typeof(MyExceptionHandler)) { logStuff = logger; nameValueCollection= attribute; }

        public override IEnumerable<TypeRegistration> GetRegistrations(string namePrefix)
        {
            yield //this doesn't compile without yield, my guess is that yield is required because the return type is enumerable etc.
            return new TypeRegistration<IExceptionHandler>(() => new MyExceptionHandler(Logger, Attribute))//<--since nulls were passed into ctor, Logger and Attribute are passed as null to MyExceptionHandler
            {
                Name = BuildName(namePrefix),
                Lifetime = TypeRegistrationLifetime.Transient
            };
        }

    }

registration in config (using Unity 2.0)

      <register type="IExceptionHandler" mapTo="MyExceptionHandler" name="Default">
        <constructor>
          <param value="ILogStuff" name="logger"></param>
          <param type="NameValueCollection" name="attribute"></param>
        </constructor>
      </register>

ILogStuff is registered and is instantiated by the Unity container correctly throughout the rest of the application, just not in the MyExceptionHandlerData constructor (so subsequently, MyExceptionHandler logger is always null).

Any suggestions?

Thanks again.


Nov 4, 2010 at 3:13 AM
Edited Nov 4, 2010 at 3:21 AM

I can see 2 ways on how to inject your ILogStuff, first is to use Container.Resolved<ILogStuff> in place of the Logger property in the GetRegistrations method:

public override IEnumerable<TypeRegistration> GetRegistrations(string namePrefix)
        {
            yield 
            return new TypeRegistration<IExceptionHandler>(() => new MyExceptionHandler(Container.Resolved<ILogStuff>())
            {
                Name = BuildName(namePrefix),
                Lifetime = TypeRegistrationLifetime.Transient
            };
        }

If your registration for the ILogStuff is done through the configuration file, you would need to load the container from the configuration file and and replace the EnterpriseLibraryContainer.Current object:

var container = new UnityContainer()
       .AddNewExtension<EnterpriseLibraryCoreExtension>()
       .LoadConfiguration();
        
EnterpriseLibraryContainer.Current = new UnityServiceLocator(container);

Another approach is the one I think you're trying to do which is to override the registration in the configuration file.

To override the type registration you specified in the GetRegistrations method in the configuration file, you should give it the correct name.  The registration name of an exception handler takes the format of

exceptionPolicyName.exceptionTypeName.exceptionHandlerName

Thus, if you have this exception policy configured:

<add name="ErrorPolicy">
        <exceptionTypes>
          <add name="All Exceptions" type="System.ArgumentException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="CustomObjects.MyExceptionHandlerData" type="CustomObjects.MyExceptionHandler, CustomObjects, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
            </exceptionHandlers>
          </add>
        </exceptionTypes>
</add>

the registration name for the MyExceptionHandler should be ErrorPolicy.All Exceptions.CustomObjects.MyExceptionHandlerData

 You would also used the code above which loads the container from the configuration and replacing the EnterpriseLibraryContainer.Current object if you used this approach.

 

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

Nov 4, 2010 at 10:08 AM

Sarah,

As always, thanks for your helpful reply. Container.Resolved<ILogStuff>() did the trick. However, I'm slightly confused with regard to your comment that "The registration name of an exception handler takes the format of exceptionPolicyName.exceptionTypeName.exceptionHandlerName" as I didn't have to modify my configuration file in order for everything to work. Is there somewhere in the help documentation that discusses Container.Resolved<T>(I couldn't find it)? I would have never guessed how to resolve this without your input! Can you point me in the right direction?

Thanks again.



        
    
Nov 5, 2010 at 12:59 AM

The registration name I was referring to was this:

<register type="IExceptionHandler" mapTo="MyExceptionHandler" name="Default">
        <constructor>
          <param value="ILogStuff" name="logger"></param>
          <param type="NameValueCollection" name="attribute"></param>
        </constructor>
      </register>

Since you used the first approach which is to make use of Container.Resolved you really didn't have to modify your config to make it work.  The registration above for the MyExceptionHandler didn't do anything to get it working and you can even delete it.  If you used the second approach, you would need to correct that registration because in the GetRegistrations method, your MyExceptionHandler was registered using the concatenation of the exception policy name, exception type name, and exception handler name with null parameters.  The registration in the configuration file is meant to override that registration so you could configure the right parameters.  But to do that, you need to use the same registration name which was used there which is the output of the BuildName(namePrefix).

 

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

Nov 8, 2010 at 9:55 AM

Sarah,

If I wanted to take the second approach, and assuming I have this in my config:

<add name="ErrorPolicy">
<exceptionTypes>
<add name="All Exceptions" type="System.ArgumentException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
postHandlingAction="ThrowNewException">
<exceptionHandlers>
<add name="CustomObjects.MyExceptionHandlerData" type="CustomObjects.MyExceptionHandler, CustomObjects, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</exceptionHandlers>
</add>
</exceptionTypes>
</add>
what portion below needs to be renamed (ref: you need to use the same registration name which was used there which is the output of the BuildName(namePrefix)) to ErrorPolicy.All Exceptions.CustomObjects.MyExceptionHandlerData

      <register type="IExceptionHandler" mapTo="MyExceptionHandler" name="Default">
        <constructor>
          <param value="ILogStuff" name="logger"></param>
          <param type="NameValueCollection" name="attribute"></param>
        </constructor>
      </register>

and how would I accomplish this:
EnterpriseLibraryContainer.Current = new UnityServiceLocator(container);
via configuration (i.e. config file)? Would <extension type="EnterpriseLibraryCoreExtension"/> (within the Unity registration element of course) suffice?
Thanks again for your input.

Nov 8, 2010 at 10:22 AM
Edited Nov 8, 2010 at 10:33 AM

You need to replace the value of the name attribute

<register type="IExceptionHandler" mapTo="MyExceptionHandler" name="ErrorPolicy.All Exceptions.CustomObjecgts.MyExceptionHandlerData">

Take note that the attribute parameter of type NameValueCollection won't get properly injected since there's no direct support to configure it in the config file.  (There are other related threads on this forum regarding that topic).  So I suggest you configure the ILogStuff as a property injection rather than in the constructor injection.

On your second question, it is not configured in the config file as what it actually does is it replaces the current configuration being used by entlib to resolve objects with the container.

 

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