0

我正在寻找一种方法来跟踪花费在数据库上的总时间,并将其合并到一个服务操作调用甚至整个会话中。由于我目前使用的是 default 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;
    }
}

然后我将在服务合同范围之外引用它,并能够在那里记录它。

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

在 IIS 上托管 WCF Web 服务时,我尝试遵循Explicitly call a service constructorHooking into wcf pipeline和一些Carlos Figueira MSDN 博客:WCF Extensibility,但没有取得多大成功。我也很难找到很多关于它的文档。换句话说,我被卡住了。

4

1 回答 1

0

我在IOperationInvoker和之间有点纠结IInstanceProvider

事实证明,对于我需要的东西来说,这IOperationInvoker相当复杂,因为我需要扩展同步和异步调用。但它的优点是它专门用于在每个方法调用之前和之后执行操作。虽然我仍然不完全确定如何将对象传递给任何服务方法,我可以用它来跟踪使用,在层次结构中较低。不幸的是,Carlos Figueira 关于 WCF 可扩展性的博客在他的示例中没有涉及到这一点(他展示了如何缓存调用)。

结果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注入管道,我们可以在服务实例释放后获取:

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可以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

public class PerformanceServiceFactory
{
    private static ConcurrentDictionary<Type, ConstructorInfo> Constructors
            = new ConcurrentDictionary<Type, ConstructorInfo>();
    public static object Create(Type type, PerformanceContext perfContext)
    {
        ConstructorInfo ctor;
        if(Constructors.TryGetValue(type, out ctor))
        {
            return InvokeConstructor(ctor, perfContext);
        }
        else if (type.IsSubclassOf(typeof(PerformanceMonitoredService))
            ||type.IsAssignableFrom(typeof(PerformanceMonitoredService)))
        {
            ConstructorInfo newCtor = type.GetConstructor( 
                new[] { typeof(PerformanceContext) }
            );
            if(Constructors.TryAdd(type, newCtor))
            {
                return InvokeConstructor(newCtor, perfContext);
            } else if(Constructors.TryGetValue(type, out ctor))
            {
                return InvokeConstructor(ctor, perfContext);
            }
        }
        throw new ArgumentException(
            $"Expected type inheritable of {typeof(PerformanceMonitoredService).Name}"}",
            "type");
    }

    private static object InvokeConstructor(ConstructorInfo ctor,
            PerformanceContext perfContext)
    {
        return ctor.Invoke(new object[] { perfContext });
    }
}
于 2016-11-23T12:16:18.870 回答