0

データベースで費やされた合計時間を追跡し、それを 1 回の Service Operation 呼び出しまたはセッション全体にマージする方法を探しています。私は現在 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 サービスをホストするときにサービス コンストラクターを明示的に呼び出し、wcf パイプラインにフックしCarlos Figueira MSDN ブログの一部: WCF Extensibilityに従ってみましたが、あまり成功しませんでした。また、一般的なドキュメントを見つけるのに苦労しています。言い換えれば、私は立ち往生しています。

4

1 に答える 1

0

IOperationInvokerと の間で少し迷っていIInstanceProviderます。

同期呼び出しと非同期呼び出しのIOperationInvoker両方を拡張する必要があるため、必要なものに対してかなり複雑であることが判明しました。ただし、各メソッド呼び出しの前後にアクションを実行するように特別に作成されているという利点があります。オブジェクトを任意のサービス メソッドに渡す方法はまだ完全にはわかりませんが、階層の下位にある使用状況を追跡するために使用できます。残念ながら、Carlos Figueira の WCF Extensibility に関するブログでは、彼の例ではこれに触れていません (彼は呼び出しをキャッシュする方法を示しています)。

を実装するのIInstanceProviderがより簡単であることが判明し、各操作の前後にアクションを実行することも可能になりましInstanceContextModePerCall。に変更するとPerSession、代わりにセッションごとに 1 回突然アクションを実行することになります。しかし、私の場合、主な目的はデータを可能な限りマージすることであるため、それは受け入れられます。


カスタムの Service クラスの 1 つで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"
    }
}

これはIServiceBehavior、注入Attributeが必要なすべてのサービスに追加されます。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 に答える