मैं अपनी wcf सेवा पर कंस्ट्रक्टर को मान कैसे दे सकता हूं?


103

मैं अपनी सेवा को लागू करने वाले वर्ग में निर्माता में मूल्यों को पारित करना चाहता हूं।

However ServiceHost only lets me pass in the name of the type to create, not what arguments to pass to its contrstructor.

मैं एक कारखाने में पारित करने में सक्षम होना चाहता हूं जो मेरी सेवा वस्तु बनाता है।

मैंने अब तक क्या पाया है:


6
मुझे डर है कि जटिलता WCF में निहित है और वहाँ बहुत कुछ नहीं है जो आप इसे कम कर सकते हैं, WCF का उपयोग न करने या इसे और अधिक उपयोगकर्ता के अनुकूल मुखौटा के पीछे छिपाने के लिए, जैसे विंडसर की WCF सुविधा यदि आप
WCOR

जवाबों:


122

आपको कस्टम के संयोजन को लागू करने की आवश्यकता होगी ServiceHostFactory, ServiceHostऔरIInstanceProvider

इस निर्माता के हस्ताक्षर के साथ एक सेवा दी:

public MyService(IDependency dep)

यहाँ एक उदाहरण है जो MyService को स्पिन कर सकता है:

public class MyServiceHostFactory : ServiceHostFactory
{
    private readonly IDependency dep;

    public MyServiceHostFactory()
    {
        this.dep = new MyClass();
    }

    protected override ServiceHost CreateServiceHost(Type serviceType,
        Uri[] baseAddresses)
    {
        return new MyServiceHost(this.dep, serviceType, baseAddresses);
    }
}

public class MyServiceHost : ServiceHost
{
    public MyServiceHost(IDependency dep, Type serviceType, params Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    {
        if (dep == null)
        {
            throw new ArgumentNullException("dep");
        }

        foreach (var cd in this.ImplementedContracts.Values)
        {
            cd.Behaviors.Add(new MyInstanceProvider(dep));
        }
    }
}

public class MyInstanceProvider : IInstanceProvider, IContractBehavior
{
    private readonly IDependency dep;

    public MyInstanceProvider(IDependency dep)
    {
        if (dep == null)
        {
            throw new ArgumentNullException("dep");
        }

        this.dep = dep;
    }

    #region IInstanceProvider Members

    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return this.GetInstance(instanceContext);
    }

    public object GetInstance(InstanceContext instanceContext)
    {
        return new MyService(this.dep);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        var disposable = instance as IDisposable;
        if (disposable != null)
        {
            disposable.Dispose();
        }
    }

    #endregion

    #region IContractBehavior Members

    public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
    }

    public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
    {
        dispatchRuntime.InstanceProvider = this;
    }

    public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
    {
    }

    #endregion
}

MyServiceHostFactory को अपनी MyService.svc फ़ाइल में पंजीकृत करें, या स्वयं-होस्टिंग परिदृश्यों के लिए सीधे कोड में MyServiceHost का उपयोग करें।

आप इस दृष्टिकोण को आसानी से सामान्य कर सकते हैं, और वास्तव में कुछ DI कंटेनरों ने आपके लिए यह पहले ही कर दिया है (क्यू: विंडसर की WCF सुविधा)।


+1 (लेकिन हाँ, # भागना भले ही अपराध का कम से कम गंभीर मामला हो, मैं अपने आप को स्पष्ट इंटरफ़ेस में परिवर्तित करता हूं: P)
Ruben Bartelink

5
मैं इसे स्वयं की मेजबानी के लिए कैसे उपयोग कर सकता हूं? मुझे CreateServiceHost पर कॉल करने के बाद एक अपवाद प्राप्त होता है। मैं केवल संरक्षित विधि को सार्वजनिक ओवरराइड ServiceHostBase CreateServiceHost (स्ट्रिंग constructorString, उरी [] baseAddresses) पर कॉल कर सकता हूं; अपवाद अपवाद संदेश था: 'ServiceHostFactory.CreateServiceHost' को वर्तमान होस्टिंग वातावरण में लागू नहीं किया जा सकता है। इस API के लिए आवश्यक है कि कॉलिंग एप्लिकेशन को IIS या WAS में होस्ट किया जाए।
लड़के

2
@ Guy मैं नमूना समस्या है। क्योंकि फ़ंक्शन है protectedमैं इसे खुद को मुख्य () से नहीं कह सकता
एंड्री Drozdyuk

1
इस दृष्टिकोण के साथ एक अंतर्निहित समस्या है, और यह है कि आपकी निर्भरता वास्तव में केवल एक बार IIS होस्ट किए गए वातावरण में बनाई गई है। ServiceHostFactory, ServiceHost, और InstanceProvider सभी केवल एक बार बनाए जाते हैं जब तक कि एप्लिकेशन पूल को पुनर्नवीनीकरण नहीं किया जाता है, जिसका अर्थ है कि आपकी निर्भरता वास्तव में प्रति कॉल ताज़ा नहीं हो सकती है (उदाहरण के लिए DbContext), जो मूल्यों के अनपेक्षित कैशिंग और निर्भरता के लंबे जीवनकाल को पूरा करता है। नहीं चाहिए। मुझे यकीन नहीं है कि यह कैसे हल करना है, किसी भी विचार?
डेविड एंडरसन

2
@MarkSeemann मैं अभी सोच रहा हूं, आपने depहर कॉन्ट्रैक्ट के इंस्टेंसप्रॉइडर में इंजेक्शन क्यों लगाया । आप कर सकता है: ImplementedContracts.Values.First(c => c.Name == "IMyService").ContractBehaviors.Add(new MyInstanceProvider(dep));जहां IMyService अपने के एक अनुबंध इंटरफेस है MyService(IDependency dep)। तो IDependencyकेवल InstanceProvider में इंजेक्ट करें जो वास्तव में इसकी आवश्यकता है।
वॉयटेक

14

आप बस अपना उदाहरण बना सकते हैं और Serviceउस उदाहरण को ServiceHostऑब्जेक्ट में पास कर सकते हैं। केवल एक चीज जो आपको करनी है वह है [ServiceBehaviour]अपनी सेवा के लिए एक विशेषता जोड़ना और सभी लौटी हुई वस्तुओं को [DataContract]विशेषता के साथ चिह्नित करना ।

यहाँ एक नकली है:

namespace Service
{
    [ServiceContract]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class MyService
    {
        private readonly IDependency _dep;

        public MyService(IDependency dep)
        {
            _dep = dep;
        }

        public MyDataObject GetData()
        {
            return _dep.GetData();
        }
    }

    [DataContract]
    public class MyDataObject
    {
        public MyDataObject(string name)
        {
            Name = name;
        }

        public string Name { get; private set; }
    }

    public interface IDependency
    {
        MyDataObject GetData();
    }
}

और उपयोग:

var dep = new Dependecy();
var myService = new MyService(dep);
var host = new ServiceHost(myService);

host.Open();

मुझे उम्मीद है कि यह किसी के लिए जीवन को आसान बना देगा।


5
यह केवल एकल के लिए काम करता है (जैसा कि संकेत दिया गया है InstanceContextMode.Single)।
जॉन रेनॉल्ड्स

11

मार्क का उत्तर IInstanceProviderसही है।

कस्टम ServiceHostFactory का उपयोग करने के बजाय आप एक कस्टम विशेषता (कहते हैं MyInstanceProviderBehaviorAttribute) का भी उपयोग कर सकते हैं । इससे दूर करें Attribute, इसे लागू करें IServiceBehaviorऔर IServiceBehavior.ApplyDispatchBehaviorजैसी विधि लागू करें

// YourInstanceProvider implements IInstanceProvider
var instanceProvider = new YourInstanceProvider(<yourargs>);

foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
    foreach (var epDispatcher in dispatcher.Endpoints)
    {
        // this registers your custom IInstanceProvider
        epDispatcher.DispatchRuntime.InstanceProvider = instanceProvider;
    }
}

फिर, विशेषता को अपनी सेवा कार्यान्वयन कक्षा में लागू करें

[ServiceBehavior]
[MyInstanceProviderBehavior(<params as you want>)]
public class MyService : IMyContract

तीसरा विकल्प: आप कॉन्फ़िगरेशन फ़ाइल का उपयोग करके सेवा व्यवहार भी लागू कर सकते हैं।


2
तकनीकी रूप से, यह भी एक समाधान की तरह दिखता है, लेकिन उस दृष्टिकोण के साथ, आप सेवा के साथ IInstanceProvider को कसकर जोड़ते हैं।
मार्क सेमन

2
बस एक दूसरा विकल्प, बेहतर क्या है पर कोई आकलन नहीं। मैंने कई बार कस्टम सर्विसहोस्टफ़ैक्ट का उपयोग किया है (विशेषकर जब आप कई व्यवहारों को पंजीकृत करना चाहते हैं)।
डैलो

1
समस्या यह है कि आप उदाहरण के लिए DI कंटेनर को केवल विशेषता निर्माणकर्ता में आरंभ कर सकते हैं .. आप मौजूद डेटा नहीं भेज सकते।
लड़के

5

मैंने मार्क के उत्तर से काम लिया, लेकिन (कम से कम मेरे परिदृश्य के लिए), यह अनावश्यक रूप से जटिल था। में से एक ServiceHostकंस्ट्रक्टर्स सेवा है, जो सीधे आपसे में पारित कर सकते हैं का एक उदाहरण स्वीकार करता है ServiceHostFactoryकार्यान्वयन।

मार्क के उदाहरण को देखने के लिए, यह इस तरह दिखाई देगा:

public class MyServiceHostFactory : ServiceHostFactory
{
    private readonly IDependency _dep;

    public MyServiceHostFactory()
    {
        _dep = new MyClass();
    }

    protected override ServiceHost CreateServiceHost(Type serviceType,
        Uri[] baseAddresses)
    {
        var instance = new MyService(_dep);
        return new MyServiceHost(instance, serviceType, baseAddresses);
    }
}

public class MyServiceHost : ServiceHost
{
    public MyServiceHost(MyService instance, Type serviceType, params Uri[] baseAddresses)
        : base(instance, baseAddresses)
    {
    }
}

12
यह काम करेगा अगर आपकी सेवा और सभी इंजेक्शन निर्भरताएं थ्रेड-सुरक्षित हैं। ServiceHost कंस्ट्रक्टर का विशेष रूप से अधिभार WCF के जीवनचक्र प्रबंधन को अनिवार्य रूप से अक्षम कर देता है। इसके बजाय, आप कह रहे हैं कि सभी समवर्ती अनुरोधों को नियंत्रित किया जाएगा instance। प्रदर्शन पर असर पड़ सकता है या नहीं। यदि आप समवर्ती अनुरोधों को संभालने में सक्षम होना चाहते हैं, तो संपूर्ण ऑब्जेक्ट ग्राफ़ थ्रेड-सुरक्षित होना चाहिए, या आपको गैर-नियतात्मक, गलत व्यवहार मिलेगा। यदि आप थ्रेड-सुरक्षा की गारंटी दे सकते हैं, तो मेरा समाधान वास्तव में, अनावश्यक रूप से जटिल है। यदि आप इसकी गारंटी नहीं दे सकते, तो मेरे समाधान की आवश्यकता है।
मार्क सीमन

3

इसे स्क्रू करें ... मैंने निर्भरता इंजेक्शन और सर्विस लोकेटर पैटर्न को मिश्रित किया (लेकिन ज्यादातर यह अभी भी निर्भरता इंजेक्शन है और यह कंस्ट्रक्टर में भी होता है जिसका मतलब है कि आप केवल-पढ़ने के लिए राज्य कर सकते हैं)।

public class MyService : IMyService
{
    private readonly Dependencies _dependencies;

    // set this before creating service host. this can use your IOC container or whatever.
    // if you don't like the mutability shown here (IoC containers are usually immutable after being configured)
    // you can use some sort of write-once object
    // or more advanced approach like authenticated access
    public static Func<Dependencies> GetDependencies { get; set; }     
    public class Dependencies
    {
        // whatever your service needs here.
        public Thing1 Thing1 {get;}
        public Thing2 Thing2 {get;}

        public Dependencies(Thing1 thing1, Thing2 thing2)
        {
            Thing1 = thing1;
            Thing2 = thing2;
        }
    }

    public MyService ()
    {
        _dependencies = GetDependencies(); // this will blow up at run time in the exact same way your IoC container will if it hasn't been properly configured up front. NO DIFFERENCE
    }
}

सेवा की निर्भरता स्पष्ट रूप से यह नेस्टेड Dependenciesवर्ग के अनुबंध में निर्दिष्ट है । यदि आप एक IoC कंटेनर का उपयोग कर रहे हैं (एक जो पहले से ही आपके लिए WCF मेस को ठीक नहीं करता है), आप इसे Dependenciesसेवा के बजाय उदाहरण बनाने के लिए कॉन्फ़िगर कर सकते हैं । इस तरह से आपको गर्म फजी एहसास होता है कि आपका कंटेनर आपको देता है जबकि WCF द्वारा लगाए गए बहुत से हुप्स के माध्यम से कूदना भी नहीं है।

मैं इस दृष्टिकोण पर कोई नींद नहीं खोने जा रहा हूं। न ही किसी और को। आखिरकार, आप IoC कंटेनर हैं, प्रतिनिधियों का एक बड़ा, मोटा, स्थिर संग्रह है जो आपके लिए सामान बनाता है। एक और क्या जोड़ रहा है?


समस्या का एक हिस्सा यह था कि मैं निर्भरता इंजेक्शन का उपयोग करके कंपनी को प्राप्त करना चाहता हूं, और अगर यह प्रोग्रामर को साफ और सरल नहीं दिखता, जिसने कभी निर्भरता इंजेक्शन का उपयोग नहीं किया था, तो निर्भरता इंजेक्शन कभी भी किसी अन्य प्रोग्रामर द्वारा उपयोग नहीं किया जाएगा। हालाँकि मैंने कई वर्षों से WCF का उपयोग नहीं किया है, और मैं इसे याद नहीं करता हूँ!
इयान रिंगरोस 10

यहाँ एक लेख एक बार संपत्ति के लिए अपने दृष्टिकोण है stackoverflow.com/questions/839788/...
रोनी Overby

0

हम इसी समस्या का सामना कर रहे थे और इसे निम्नलिखित तरीके से हल किया है। यह एक सरल उपाय है।

विजुअल स्टूडियो में बस एक सामान्य WCF सेवा एप्लिकेशन बनाएं और इसे हटा दें। .Cs फ़ाइल को जगह में छोड़ दें (बस इसका नाम बदलें) और उस cs फ़ाइल को खोलें और अपने मूल वर्ग नाम के साथ इंटरफ़ेस का नाम बदलें जो सेवा तर्क को लागू करता है (इस तरह सेवा वर्ग विरासत का उपयोग करता है और आपके वास्तविक कार्यान्वयन को बदल देता है)। एक डिफ़ॉल्ट कंस्ट्रक्टर जोड़ें जो बेस क्लास के कंस्ट्रक्टर्स को कॉल करता है, जैसे:

public class Service1 : MyLogicNamespace.MyService
{
    public Service1() : base(new MyDependency1(), new MyDependency2()) {}
}

MyService बेस क्लास सेवा का वास्तविक कार्यान्वयन है। इस बेस क्लास में एक पैरामीटर रहित कंस्ट्रक्टर नहीं होना चाहिए, लेकिन केवल ऐसे कंस्ट्रक्टर हैं जो मापदंडों पर निर्भर करते हैं।

सेवा को मूल MyService के बजाय इस वर्ग का उपयोग करना चाहिए।

यह एक सरल उपाय है और एक आकर्षण की तरह काम करता है :-D


4
आपने Service1 को उसकी निर्भरता से नहीं हटाया है, जो बिंदु की तरह था। आपने बस सेवा 1 के लिए कंस्ट्रक्टर में निर्भरता को तुरंत समाप्त कर दिया है, जिसे आप आधार वर्ग के बिना कर सकते हैं।
23

0

यह एक बहुत ही उपयोगी समाधान था - विशेष रूप से किसी के लिए जो एक नौसिखिया डब्ल्यूसीएफ कोडर है। मैं उन किसी भी उपयोगकर्ता के लिए एक छोटी सी टिप पोस्ट करना चाहता था जो आईआईएस-होस्टेड सेवा के लिए इसका उपयोग कर रहे हों। MyServiceHost को WebServiceHost विरासत में मिली है , न कि केवल ServiceHost को।

public class MyServiceHost : WebServiceHost
{
    public MyServiceHost(MyService instance, Type serviceType, params Uri[] baseAddresses)
        : base(instance, baseAddresses)
    {
    }
}

यह IIS में आपके समापन बिंदुओं के लिए सभी आवश्यक बाइंडिंग, आदि बनाएगा।


-2

मैं अपने प्रकार के स्थिर चर का उपयोग करता हूं। यकीन नहीं है कि यह सबसे अच्छा तरीका है, लेकिन यह मेरे लिए काम करता है:

public class MyServer
{   
    public static string CustomerDisplayName;
    ...
}

जब मैं सेवा होस्ट होस्ट करता हूं तो मैं निम्नलिखित कार्य करता हूं:

protected override void OnStart(string[] args)
{
    MyServer.CustomerDisplayName = "Test customer";

    ...

    selfHost = new ServiceHost(typeof(MyServer), baseAddress);

    ....
}

5
स्टेटिक / सिंगलनेट्स दुष्ट हैं! - देखें stackoverflow.com/questions/137975/…
अमर ब्लू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.