将对象注入WCF管道,在每次OperationContract调用之前和之后进行工作

时间:2016-11-23 12:16:19

标签: c# wcf logging wcf-extensions

我正在寻找一种方法来跟踪在数据库上花费的总时间,并将其合并到一个服务操作调用或甚至整个会话中。由于我当前正在使用默认的PerCall InstanceContextMode,所以每次调用服务方法时都会调用Service类的构造函数,所以我想到的是像挂钩之前调用的一些管道方法并在每个服务方法之后,调用非空构造函数。然后注入一个对象以进一步传递到层次结构中:

[ServiceContract]
public interface IJobsService { ... }
public partial class JobsService : IJobsService
{
    public PerformanceContext PerformanceContext { get; private set; }
    JobsService() { ... }
    JobsService(PerformanceContext context) : this()
    {
        RequestContext = context;
    }
}


public class PerformanceContext
{
    private object syncObj = new object();
    private long? userID;
    public long? UserID { ... }
    public string Source { get; set; }
    private long totalTicksUsed = 0;
    public long TotalTicksUsed
    {
        get { return totalTicksUsed; }
        private set { totalTicksUsed = value; }
    }
    public void AddTicksUsed(long ticks, long? userID)
    {
        Interlocked.Add(ref totalTicksUsed, ticks);
        UserID = userID;
    }
}

然后我会在服务合约的范围之外引用它,并且能够在那里记录它。

就像现在一样,我实现这种行为的“最简单”的方法是在每个服务方法的最后调用一个日志功能,但如果有更好的方法,我觉得它不是很漂亮

我已尝试关注Explicitly calling a service constructor when hosting a WCF web service on IISHooking into wcf pipeline和部分Carlos Figueira MSDN blog: WCF Extensibility,但未取得多大成功。我也很难找到一些关于它的文档。换句话说,我被卡住了。

1 个答案:

答案 0 :(得分:0)

我在IOperationInvokerIInstanceProvider之间有点挣扎。

IOperationInvoker已经证明对我的需要相当复杂,因为我需要扩展同步和异步调用。但它的优点是它专门用于在每次方法调用之前和之后执行操作。虽然我还不完全确定如何将对象传递给任何可用于跟踪使用的服务方法,但在层次结构中较低。遗憾的是,Carlos Figueira's blog on WCF Extensibility在他的示例中并没有触及这一点(他展示了如何缓存呼叫)。

IInstanceProvider对我来说更加简单,并且还可以在每次操作之前和之后执行操作 - 只要InstanceContextModePerCall。如果我要将其更改为PerSession,我会突然每次会话执行一次操作。但就我而言,这是可以接受的,因为主要目标是尽可能合并数据:

我的一个Service类,其中包含自定义ServiceBehavior Attribute并继承了一个抽象类型,该抽象类型规定我们有一个带PerformanceContext的构造函数:

[ServiceContract]
public interface IJobsService { ... }

[PerformanceInstanceProviderBehavior]
public partial class JobsService : PerformanceMonitoredService, IJobsService
{
    public PerformanceContext PerformanceContext { get; protected set; }
    JobsService() { ... }
    JobsService(PerformanceContext perfContext) : this()
    {
        PerformanceContext = perfContext;
    }
    ...
}

IInstanceProvider允许调用特定的构造函数并将IExtension注入到管道中,我们可以在Service实例发布后获取它:

public class ServiceInstanceProvider : IInstanceProvider
{
    public Type ServiceType { get; set; }
    public ServiceInstanceProvider(Type serviceType) { ServiceType = serviceType; }
    public object GetInstance(InstanceContext instanceContext)
    {
        return this.GetInstance(instanceContext, null);
    }

    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        var perfContext = new PerformanceInstanceContext();
        instanceContext.Extensions.Add(new PerformanceInstanceExtension(perfContext));
        return ServiceFactory.Create(ServiceType, perfContext);
        //return new JobsService(perfContext);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        var perfContext = (instanceContext.Extensions.FirstOrDefault(ice =>
                          ice is PerformanceInstanceExtension)
                          as PerformanceInstanceExtension
                          )?.PerformanceContext;
        //Handle the object which has been through the pipeline
        //Note (IErrorHandler):
        //This is called after "ProvideFault", but before "HandleError"
    }
}

IServiceBehaviorAttribute将添加到需要PerformanceContext注入的所有服务中。

public class PerformanceInstanceProviderBehaviorAttribute : Attribute, IServiceBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
            ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
        {
            foreach (EndpointDispatcher ed in cd.Endpoints)
            {
                if (!ed.IsSystemEndpoint)
                {
                    //Each Service Type is getting their own InstanceProvider,
                    //So we can pass the type along,
                    //and let a factory create the appropriate instances:
                    ed.DispatchRuntime.InstanceProvider =
                        new ServiceInstanceProvider(serviceDescription.ServiceType);
                }
            }
        }
    }

    ...
}

我们可以通过实例提供程序管道附加到IExtension的{​​{1}}:

InstanceContext

应允许此注入的抽象服务类型:

public class PerformanceInstanceExtension : IExtension<InstanceContext>
{
    public PerformanceInstanceExtension()
    {
        PerformanceContext = new PerformanceContext();
    }

    public PerformanceInstanceExtension(PerformanceContext perfContext)
    {
        PerformanceContext = perfContext;
    }
    public PerformanceContext PerformanceContext { get; private set; }

    public void Attach(InstanceContext owner) {}
    public void Detach(InstanceContext owner) {}
}

继承public abstract class PerformanceMonitoredService { public abstract PerformanceContext PerformanceContext { get; protected set; } public PerformanceMonitoredService() {} public PerformanceMonitoredService(PerformanceContext perfContext) {} } 的服务工厂:

PerformanceMonitoredService