.net - डब्ल्यूसीएफ सेवा में एसिंक/प्रतीक्षा का उपयोग करते समय ऑपरेशन कॉन्टेक्स्ट। कंटेंट पहले इंतजार के बाद शून्य है




wcf .net-4.5 (5)

मैं डब्ल्यूसीएफ में कुछ सेवा विधियों को लागू करने के लिए .NET 4.5 में एसिंक / प्रतीक्षा पैटर्न का उपयोग कर रहा हूं। उदाहरण सेवा:

अनुबंध:

[ServiceContract(Namespace = "http://async.test/")]
public interface IAsyncTest
{
    Task DoSomethingAsync();
}

कार्यान्वयन:

MyAsyncService : IAsyncTest
{
    public async Task DoSomethingAsync()
    {
        var context = OperationContext.Current; // context is present

        await Task.Delay(10);

        context = OperationContext.Current; // context is null
    }
}

मेरी समस्या यह है कि पहले OperationContext.Current कॉन्टेक्स्ट का await बाद। OperationContext.Current वापस लौटता है और मैं OperationContext.Current कॉन्टेक्स्ट.कुरेंट.इनकमिंग null तक नहीं पहुंच सकता।

इस सरल उदाहरण में यह कोई समस्या नहीं है क्योंकि मैं await से पहले संदर्भ को कैप्चर कर सकता हूं। लेकिन वास्तविक दुनिया के मामले में OperationContext.Current कॉन्टेक्स्ट। कॉलेंट को कॉल स्टैक के अंदर गहरे से एक्सेस किया जा रहा है और मैं वास्तव में संदर्भ को आगे बढ़ाने के लिए बहुत सारे कोड को बदलना नहीं चाहता हूं।

क्या स्टैक को मैन्युअल रूप से पास किए बिना await बिंदु के बाद ऑपरेशन संदर्भ प्राप्त करने का कोई तरीका है?


अद्यतन: जैसा कि नीचे दी गई टिप्पणियों में बताया गया है, यह समाधान थ्रेड सुरक्षित नहीं है, इसलिए मुझे लगता है कि ऊपर चर्चा किए गए समाधान अभी भी सबसे अच्छा तरीका है।

मैं अपने डी कंटेनर (Application_BeginRequest) में HttpContext को पंजीकृत करके समस्या के साथ घूमता हूं और जब भी मुझे इसकी आवश्यकता होती है तो इसे हल करें।

रजिस्टर:

this.UnityContainer.RegisterInstance<HttpContextBase>(new HttpContextWrapper(HttpContext.Current));

का समाधान करें:

var context = Dependencies.ResolveInstance<HttpContextBase>();

ऐसा लगता है कि नेट 4.6.2 में तय किया गया है। घोषणा देखें


यह दुर्भाग्यपूर्ण है कि यह काम नहीं करता है और हम भविष्य में रिलीज में फिक्स आउट करने के बारे में देखेंगे।

इस बीच, वर्तमान धागे के संदर्भ को फिर से लागू करने का एक तरीका है ताकि आपको ऑब्जेक्ट को पास करने की आवश्यकता न हो:

    public async Task<double> Add(double n1, double n2)
    {

        OperationContext ctx = OperationContext.Current;

        await Task.Delay(100);

        using (new OperationContextScope(ctx))
        {
            DoSomethingElse();
        }
        return n1 + n2;
    }  

उपर्युक्त उदाहरण में, DoSomethingElse () विधि के पास ऑपरेशन कॉन्टेक्स्टकंटेंट तक पहुंच होगी।


यहां नमूना SynchronizationContext कार्यान्वयन है:

public class OperationContextSynchronizationContext : SynchronizationContext
{
    private readonly OperationContext context;

    public OperationContextSynchronizationContext(IClientChannel channel) : this(new OperationContext(channel)) { }

    public OperationContextSynchronizationContext(OperationContext context)
    {
        OperationContext.Current = context;
        this.context = context;
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        OperationContext.Current = context;
        d(state);
    }
}

और उपयोग:

var currentSynchronizationContext = SynchronizationContext.Current;
try
{
    SynchronizationContext.SetSynchronizationContext(new OperationContextSynchronizationContext(client.InnerChannel));
    var response = await client.RequestAsync();
    // safe to use OperationContext.Current here
}
finally
{
    SynchronizationContext.SetSynchronizationContext(currentSynchronizationContext);
}

सौभाग्य से हमारे लिए, हमारे वास्तविक जीवन सेवा कार्यान्वयन को Unity आईओसी कंटेनर के माध्यम से तुरंत चालू किया जाता है। इससे हमें एक IWcfOperationContext बनाने की अनुमति IWcfOperationContext जो कि IWcfOperationContext लिए कॉन्फ़िगर किया गया था जिसका अर्थ यह है कि हमारे RealService प्रत्येक उदाहरण के लिए WcfOperationContext का केवल एक उदाहरण होगा।
WcfOperationContext के WcfOperationContext हम OperationContext.Current WcfOperationContext कैप्चर करते हैं और फिर उन सभी स्थानों को जिन्हें IWcfOperationContext से प्राप्त होता है। यह वास्तव में स्टीफन क्लेरी ने अपने जवाब में क्या सुझाव दिया है।





async-await