كيف أقوم بتمرير القيم إلى المُنشئ في خدمة wcf الخاصة بي؟

StackOverflow https://stackoverflow.com/questions/2454850

سؤال

أرغب في تمرير القيم إلى المنشئ في الفصل الذي ينفذ خدمتي.

ومع ذلك، تتيح لي ServiceHost فقط تمرير اسم النوع المراد إنشاؤه، وليس الوسائط التي يجب تمريرها إلى مُنشئها.

أود أن أكون قادرًا على المرور في مصنع يقوم بإنشاء كائن الخدمة الخاص بي.

ما وجدته حتى الآن:

هل كانت مفيدة؟

المحلول

ستحتاج إلى تنفيذ مجموعة من العرف 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 في وندسور).

نصائح أخرى

يمكنك ببساطة إنشاء ومثيل الخاص بك 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();

آمل أن يجعل هذا الحياة أسهل لشخص ما.

إجابة مارك مع 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

الخيار الثالث:يمكنك أيضًا تطبيق سلوك الخدمة باستخدام ملف التكوين.

لقد عملت من إجابة مارك، ولكن (بالنسبة للسيناريو الخاص بي على الأقل)، كان الأمر معقدًا بلا داع.واحد من 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)
    {
    }
}

المسمار... لقد مزجت حقن التبعية وأنماط محدد موقع الخدمة (ولكن في الغالب لا يزال حقن التبعية ويتم حتى في المنشئ مما يعني أنه يمكن أن يكون لديك حالة للقراءة فقط).

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 الخاصة بك عبارة عن مجموعة كبيرة وثابتة من المندوبين الذين يقومون بإنشاء الأشياء لك.ما إضافة واحد آخر؟

كنا نواجه نفس المشكلة وقمنا بحلها بالطريقة التالية.إنه حل بسيط.

في Visual Studio، ما عليك سوى إنشاء تطبيق خدمة WCF عادي وإزالة واجهته.اترك ملف .cs في مكانه (فقط أعد تسميته) وافتح ملف cs واستبدل اسم الواجهة باسم فئتك الأصلية التي تنفذ منطق الخدمة (بهذه الطريقة تستخدم فئة الخدمة الوراثة وتستبدل التنفيذ الفعلي).أضف منشئًا افتراضيًا يستدعي منشئي الفئة الأساسية، مثل هذا:

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

فئة MyService الأساسية هي التنفيذ الفعلي للخدمة.لا ينبغي أن تحتوي هذه الفئة الأساسية على مُنشئ بدون معلمات، ولكن فقط مُنشئات ذات معلمات تقبل التبعيات.

يجب أن تستخدم الخدمة هذه الفئة بدلاً من MyService الأصلية.

إنه حل بسيط ويعمل كالسحر :-D

كان هذا حلاً مفيدًا جدًا - خاصة بالنسبة لشخص مبتدئ في برمجة WCF.لقد أردت نشر نصيحة صغيرة لأي مستخدم قد يستخدم هذا لخدمة مستضافة على IIS.يحتاج MyServiceHost إلى أن يرث WebServiceHost, ، وليس فقط ServiceHost.

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

سيؤدي هذا إلى إنشاء جميع الارتباطات الضرورية وما إلى ذلك لنقاط النهاية الخاصة بك في IIS.

أستخدم المتغيرات الثابتة من النوع الخاص بي.لست متأكدًا مما إذا كانت هذه هي الطريقة الأفضل، لكنها تناسبني:

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

عندما أقوم بإنشاء مثيل لمضيف الخدمة، أفعل ما يلي:

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

    ...

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

    ....
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top