Unity interception: ICallHandler executes twice

Topics: Policy Injection Application Block
Aug 25, 2010 at 4:42 PM

Hi!

Im using Unity 2.0 interception with policy injection and virtual method interceptor.

Problem is, that my policy injection call handler executes twice when I expect it to execute only once. When I debugged the problem it looks that there are two behaviors in InterceptionBehaviorPipeline.interceptionBehaviors list. Each of them contains one policy injection call handler - my custom handler.

I know that there is a known issue with double executing ICallHandler when used with an interface (http://entlib.codeplex.com/wikipage?title=Why%20does%20an%20ICallHandler%20execute%20twice%20when%20used%20with%20an%20interface%3f&version=4), but this is not my case as I'm not using an interface with intercepted type.

Interesting is, that when I switch from virtual method interceptor to interface interceptor (and use interface with my intercepted type), everything works well. But this is not a solution for me, as nature of my problem does not allow me to use instance interception.

Is this another bug, or am I missing something?

Thank you very much in advance!

Jaro

Code:

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using Microsoft.Practices.Unity.InterceptionExtension;

namespace ConsoleApplication1
{
    public class MyCallHandler : ICallHandler
    {

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            return getNext()(input, getNext);
        }

        public int Order { get; set; }
    }

    public class ClassToBeIntercepted 
    {
        public virtual void MethodToBeIntercepted()
        {
            return;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IUnityContainer container = new UnityContainer().LoadConfiguration();
            ClassToBeIntercepted classToBeIntercepted = container.Resolve<ClassToBeIntercepted>();
            
            classToBeIntercepted.MethodToBeIntercepted();
        }
    }
}
Configuration
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/>
    <container>
      <extension type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity.EnterpriseLibraryCoreExtension, Microsoft.Practices.EnterpriseLibrary.Common"/>

      <extension type="Interception" />

      <register
          type="ConsoleApplication1.ClassToBeIntercepted, ConsoleApplication1"
          mapTo="ConsoleApplication1.ClassToBeIntercepted, ConsoleApplication1">
        <interceptor type="VirtualMethodInterceptor"/>
        <policyInjection/>
      </register>

      <interception>
        <policy name="addDataAccessTypes">
          <matchingRule name="MemberNameMatch" type="MemberNameMatchingRule">
            <constructor>
              <param name="nameToMatch">
                <value
                  value="MethodToBeIntercepted"
                  typeConverter="System.ComponentModel.StringConverter, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
              </param>
            </constructor>
          </matchingRule>
          <callHandler name="MyCallHandler" type="ConsoleApplication1.MyCallHandler, ConsoleApplication1"/>

        </policy>
      </interception>

    </container>

  </unity>

 

Aug 26, 2010 at 6:08 AM

Hi,

Try removing this extension from your config

<extension type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity.EnterpriseLibraryCoreExtension, Microsoft.Practices.EnterpriseLibrary.Common"/>

and see if it still execute twice.

Gino Terrado
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com

Aug 26, 2010 at 7:11 AM

Hi Gino,

thanks for your reply. If I remove the line from configuration, it executes only once. Great!

But I have two important questions:

  • Is it possible to force ICallHandler to execute only once and use Enterprise library at the same time? Adding enterprise library extension to unity container by code (not by configuration) does not help.
  • What's the magic behind the step you suggested? I can't see any connection between adding enterprise library extension of unity container and correct execution of ICallHandler.

Thanks!

Jaro

Aug 26, 2010 at 6:05 PM

The EnterpriseLibraryCoreExtension also adds the Interception extension, so you don't need to add them both. Just add the Entlib one and you should be fine.

Although I'm not sure what that fixed it - I will need to investigate further and figure out what's going on.

 

 

Aug 27, 2010 at 7:45 AM

Hi Chris,

thanks a lot for your post! It solved my problem.

Anyway, I'm starting to be upset by "side effects" of certain configuration settings that cause errors. I worked with Spring.net before and it worked as expected. Because I'm MS enthusiast I decided to try EntLib + Unity. As I spent lot of man days (=> lot of money of my employer) on resolving issues similar to this one, I'm really considering switching back to Spring.net to prevent another surprises.

Examples of issues we were facing:

  • Unity error messages are really impressive (something like "Failed to load assembly. HRESULT: ..." really helps you). In case of error in configuration you have to attach unity sources to your solution to find out which registration is problematic.
  • Under some circumstances usage of name attribute of register element in unity xml configuration causes dependencies of registered object not to be loaded
  • Shorthand syntax for generics ([]) does not work (error message: "The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)." - really descriptive). By accident I switched to CLR generics syntax (`1) and it started to work. After debugging I found out that using shorthand generic syntax caused, that unity tried to load type "{my type definition}, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", where {my type definition} si fully qualified type name including assembly.
  • Logging application block does not log to file when it does not have permissions to write to a file. What's bad is, that it does not throw any exception - so you simply do not know about the problem. This typically occurs when logging to a text file from web application. Cause of this problem - very famous antipattern: empty catch block somewhere in EntLib code.

So instead of development I feel like if I was paid for EntLib and Unity testing.

Anyway, thanks for your fix!

Jaro

Aug 27, 2010 at 10:03 PM

Jaro,

I'm sorry you feel that way. If Spring.NET solves your problems, go use it. As far as your comments go, just some responses:

On unity error messages, I assume you're talking about when you're using the configuration file? I agree with you - unfortunately, the .NET configuration system fights you the entire way. In order for things like aliases to work, we have to defer type lookup until after the entire config section is loaded. Unfortunately, the only time the configuration system will tell you if something is bad is while it's deserializing. It doesn't make file line numbers directly available anywhere, so I can't get better information even though I desperately want to.

With the usage of the name attribute, I expect you're hitting Unity's policy of "build by default if not registered." When given a type/name pair, the container looks for configuration under that type/name pair. If there isn't one, the defaults kick in and it calls the default constructor. It's not broken, that's just the way it works, and your preconceptions based on the way Spring.NET works may be coloring your expectations here.

The shorthand syntax for generics DOES work - everywhere except when defining the type for an alias. This was deliberate. This behavior is documented under the <alias> element in the config schema, I'll see if we can add a note in the Generic type shorthand section. In practice, I expect the usages of aliases to go way down over time with the addition of namespace & assembly searching anyway.

The logging block quietly eating exceptions was a deliberate design choice. The rationale was that a problem with logging should never take down the app, period (this is the same policy log4net uses, by the way). There is a special source in the configuration that you can set so that errors in the logging block will get logged, but if that fails too you're right, you get nothing. That's the way it's designed.

 

Aug 29, 2010 at 9:01 PM

Chris,

thanks for valuable comments, I'm sending some notes.

With the usage of the name attribute, I expect you're hitting Unity's policy of "build by default if not registered." When given a type/name pair, the container looks for configuration under that type/name pair. If there isn't one, the defaults kick in and it calls the default constructor. It's not broken, that's just the way it works, and your preconceptions based on the way Spring.NET works may be coloring your expectations here.

I'm not sure if we are talking about same thing. I'm talking about that under certain circumstances this doesn't work:

<register
    name="DaoSupport"
    type="... interface name ..., ... assembly name ..."
    mapTo="... class name ..., ... assembly name ...">
        <property name="Database" />
</register>

while this works:

<register
    type="... interface name ..., ... assembly name ..."
    mapTo="... class name ..., ... assembly name ...">
        <property name="Database" />
</register>

What doesn't work is that property with name Database is not initialized despite the fact that you are able to get Database object from the container in other way (e.g. container.Resolve(...)). Database is Database object from EntLib Data Access App Block. If you want, I can distill a working example for you.

The shorthand syntax for generics DOES work - everywhere except when defining the type for an alias. This was deliberate. This behavior is documented under the <alias> element in the config schema, I'll see if we can add a note in the Generic type shorthand section. In practice, I expect the usages of aliases to go way down over time with the addition of namespace & assembly searching anyway.

  • OK, I'm not saying that shorthand syntax is not working at all. Again, I can distill an example where unity does not load configuration with shorthand syntax and is able to load configuration after one change - switch from shorthand to CLR syntax.
  • Yes, shorthand syntax for generics does not work with type aliases. Despite you wrote it is documented, I have not found this in documentation of <alias> elemnt in the config schema. I had to find out this fact by experimeting. http://msdn.microsoft.com/en-us/library/ff660914(PandP.20).aspx#config_aliases

The logging block quietly eating exceptions was a deliberate design choice. The rationale was that a problem with logging should never take down the app, period (this is the same policy log4net uses, by the way). There is a special source in the configuration that you can set so that errors in the logging block will get logged, but if that fails too you're right, you get nothing. That's the way it's designed.

Interesting. I really didn't know that this behavior is deliberate and that log4net uses the same strategy. In my opinion it should be the decision/responsibility of application developer wether to eat specific exceptions or not.

Well, I'm sorry for polluting original discussion with issues that do not belong to original issue (probably).

Jaro

Aug 30, 2010 at 6:00 AM

Jaro,

Please send on those repro cases. The situations you talk about should work; I've never seen them fail, so if they do there's a serious bug somewhere. You can either send them to me directly or post them as an issue in the bug tracker here.

As for the documentation, on the page you linked, under the "type" attribute it says:

The type that the alias refers to. This name must be a CLR type name. Aliases or automatic type lookups do not apply with this attribute. This attribute is required.

(I added the italics). I agree though that this could be a lot more strongly, clearly stated.

 

Aug 30, 2010 at 9:19 PM

Chris,

issues are posted here

http://entlib.codeplex.com/workitem/28720

and here

http://entlib.codeplex.com/workitem/28721

Jaro

Aug 31, 2010 at 12:56 AM

Jaro,

Thanks, I've added my comments to the issues you posted. Not bugs from my perspective (well, at least one doc bug definitely, but not a code issue).