AOP e WCF

Dopo il post precedente, in cui ho descritto l’utilizzo di AOP in un servizio WCF, ho ricevuto delle richieste relativamente al modo in cui ho realizzato questo tipo di implementazione.
Di seguito potete trovare il codice necessario alla realizzazione di un Custom Behavior per WCF che instanzi un proxy AOP in sostituzione al servizio WCF, e la rispettiva configurazione per utilizzarlo ed aggiungergli Interceptor.
Per ovvi motivi non posso postare il codice che ha permesso l’invalidazione della cache, ma la creazione di un Interceptor simile è veramente banale.

public class AOPInstanceServiceElement : BehaviorExtensionElement
{
    protected override object CreateBehavior()
    {
        return new AOPInstanceServiceBehavior();
    }

    public override Type BehaviorType
    {
        get
        {
            return typeof(AOPInstanceServiceBehavior);
        }
    }
}

public class AOPInstanceServiceBehavior : IServiceBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
        {
            ChannelDispatcher cd = cdb as ChannelDispatcher;
            if (cd != null)
            {
                foreach (EndpointDispatcher ed in cd.Endpoints)
                {
                    ed.DispatchRuntime.InstanceProvider = new AOPInstanceProvider(serviceDescription.ServiceType);
                }
            }
        }
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
}

public class AOPInstanceProvider : IInstanceProvider
{
    private readonly Type serviceType;

    public AOPInstanceProvider(Type serviceType)
    {
        this.serviceType = serviceType;
    }

    public object GetInstance(InstanceContext instanceContext)
    {
        return GetInstance(instanceContext, null);
    }

    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        IApplicationContext context = ContextRegistry.GetContext();

        string proxyName = serviceType.FullName;
        if (!context.ContainsObject(proxyName))
            throw new ArgumentException(string.Format("There must exist exactly one <object> definition for the {0} service in the Spring configuration", serviceType.Name));

        return context.GetObject(proxyName);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        if (instance is IDisposable)
        {
            ((IDisposable)instance).Dispose();
        }
    }
}


public class MyLogAdvice : IMethodInterceptor
{
    public object Invoke(IMethodInvocation invocation)
    {
        string className = invocation.TargetType.FullName;

        Logger.Info(className, string.Format("Start Method : {0}", invocation.Method.Name));

        object result = invocation.Proceed();

        Logger.Info(className, string.Format("End Method : {0}", invocation.Method.Name));

        return result;
    }
}

Configurazione del servizio:

<spring>
    <context>
        <resource uri="config://spring/objects" />
    </context>
    <objects xmlns="http://www.springframework.net">
        <object id="logAdvice" type="CommunityDays.WCF.AOP.LogAdvice.MyLogAdvice, CommunityDays.WCF.AOP.LogAdvice" />
        <object id="rewardingAdvice" type="CommunityDays.WCF.AOP.RewardingAdvice.MyRewardingAdvice, CommunityDays.WCF.AOP.RewardingAdvice" />
        <object id="CommunityDays.WCF.AOP.Service.Service1" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
            <property name="Target">
                <ref object="Service1AOP" />
            </property>
            <property name="InterceptorNames">
                <list>
                    <value>logAdvice</value>
                </list>
            </property>
        </object>
        <object id="Service1AOP" type="CommunityDays.WCF.AOP.Service.Service1, CommunityDays.WCF.AOP.Service" />
    </objects>
</spring>

<system.serviceModel>
    
    <extensions>
        <behaviorExtensions>
            <!-- ocio a non togliere mai gli spazi dopo le , - usare sempre anche il version number - è un bug di WCF -->
            <add name="aopInstanceBehavior" type="CommunityDays.WCF.AOP.Services.Behaviors.AOPInstanceServiceElement, CommunityDays.WCF.AOP.Services.Behaviors, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
        </behaviorExtensions>
    </extensions>

    <services>
        <service name="CommunityDays.WCF.AOP.Service.Service1" behaviorConfiguration="MyServiceConfiguration">
            <host>
                <baseAddresses>
                    <add baseAddress="net.pipe://localhost/Service1"/>
                    <add baseAddress="net.tcp://localhost:7002" />
                </baseAddresses>
            </host>
            <endpoint address="Service1" binding="netTcpBinding" contract="CommunityDays.WCF.AOP.Contracts.IService1" />
            <endpoint binding="netNamedPipeBinding" bindingConfiguration="MyNamedPipeBinding" contract="CommunityDays.WCF.AOP.Contracts.IService1" />
            <endpoint address="Service1/mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
        </service>
    </services>

    <bindings>
        <netNamedPipeBinding>
            <binding name="MyNamedPipeBinding" >
                <security mode="None">
                </security>
            </binding >
        </netNamedPipeBinding>
    </bindings>

    <behaviors>
        <serviceBehaviors>
            <behavior name="MyServiceConfiguration">
                <serviceMetadata />
                <aopInstanceBehavior />
            </behavior>
        </serviceBehaviors>
    </behaviors>
    
</system.serviceModel>

Download esempio qui

Ciauz


Comments