سؤال

كيف يمكنني إخبار خدمة WCF بما يجب استخدامه عند نقل البيانات مرة أخرى إلى العميل؟

أعلم أنه يمكنني استخدام [ServiceKnownType] السمة ، التي تجعل مكالمة الخدمة تعمل بشكل جيد من خادم اختبار WCF ، ومع ذلك لا يزال فشل من العميل. نسيت شيئا ما هنا؟

[OperationContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
BaseClassZ GetObject();

رسالة الخطأ من العميل هي:

{"element 'http://schemas.datacontract.org/2004/07/baseclassz" يحتوي ليس لديه أي معرفة بأي نوع يرسم هذا الاسم. فكر في استخدام DataContRactResolver أو إضافة النوع المقابل لـ "الفك الفرعي" إلى قائمة الأنواع المعروفة - على سبيل المثال ، باستخدام سمة المعروفة أو عن طريق إضافته إلى قائمة الأنواع المعروفة انتقل إلى DataContractSerializer. "}

تسلسل/إزالة الكائن على خادم WCF باستخدام DataContractSerializer وقائمة من الأنواع المعروفة تعمل بشكل جيد.

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

[DataContract]
[KnownType(typeof(SubClassA))]
[KnownType(typeof(SubClassB))]
public class BaseClassZ 
{
    ...
}
هل كانت مفيدة؟

المحلول

لتجنب ردع رمز الخدمة الخاص بك ، ضع الأنواع المعروفة في Web.Config من الخدمة:

<system.runtime.serialization>
    <dataContractSerializer>
        <declaredTypes>
            <add type="SomeNs.BaseClassZ, SomeAssembly">
                <knownType type="SomeNs.SubClassA, SomeAssembly" />
                <knownType type="SomeNs.SubClassB, SomeAssembly" />
            </add>
        </declaredTypes>
    </dataContractSerializer>
</system.runtime.serialization>

إذا كنت ترغب في القيام بذلك حسب الرمز ، فأنت بحاجة إلى استخدام هذه السمة على واجهة الخدمة وليس على طريقة التشغيل ، لكنني أفضل النهج التصريحي:

[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IFoo
{
    [OperationContract]
    BaseClassZ GetObject();
}

تحديث:

لقد وضعت أ عينة مشروع توضيح استخدام web.config لتكوين الأنواع المعروفة وهو نهجي المفضل. وآخر عينة مشروع إظهار النهج الثاني.


تحديث 2:

بعد النظر في الكود المحدث مع عميل تطبيق Silverlight ، نلاحظ التعريف التالي:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
public interface IService1 {

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetMany", ReplyAction="http://tempuri.org/IService1/GetManyResponse")]
    System.IAsyncResult BeginGetMany(System.AsyncCallback callback, object asyncState);

    System.Collections.ObjectModel.ObservableCollection<MyCommonLib.BaseClassZ> EndGetMany(System.IAsyncResult result);

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetSingle", ReplyAction="http://tempuri.org/IService1/GetSingleResponse")]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassA))]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassB))]
    System.IAsyncResult BeginGetSingle(System.AsyncCallback callback, object asyncState);

    MyCommonLib.BaseClassZ EndGetSingle(System.IAsyncResult result);
}

لاحظ كيف BeginGetSingle تحتوي الطريقة على سمات النوع المعروفة بينما BeginGetMany الطريقة لا. في الواقع ، يجب وضع تلك السمات على تعريف الخدمة بحيث يبدو الفصل هكذا.

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassA))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassB))]
public interface IService1
{
    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetMany", ReplyAction="http://tempuri.org/IService1/GetManyResponse")]
    System.IAsyncResult BeginGetMany(System.AsyncCallback callback, object asyncState);

    System.Collections.ObjectModel.ObservableCollection<MyCommonLib.BaseClassZ> EndGetMany(System.IAsyncResult result);

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetSingle", ReplyAction="http://tempuri.org/IService1/GetSingleResponse")]
    System.IAsyncResult BeginGetSingle(System.AsyncCallback callback, object asyncState);

    MyCommonLib.BaseClassZ EndGetSingle(System.IAsyncResult result);
}

نظرًا لأن هذه فئة مُصنوعة تلقائيًا يمكن أن يكون هناك خطأ في slsvcutil.exe و svcutil.exe لأنه يظهر نفس السلوك. وضع سمات النوع المعروفة في مكانها الصحيح يحل المشكلة. تكمن المشكلة في أن هذه الفئة تلقائيًا بواسطة أداة ، وإذا حاولت تجديدها من WSDL ، فسوف تعبث مرة أخرى.

لذلك يبدو أنه إذا كان لديك تعريف الخدمة التالية:

[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IService1
{
    [OperationContract]
    BaseClassZ[] GetMany();

    [OperationContract]
    BaseClassZ GetSingle();
}

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

نصائح أخرى

قضيت ساعات اليوم على ما يمكنني قوله ، كما يمكنني أن أقول ، هو نفس المشكلة بالضبط. كان الحل بالنسبة لي هو استخدام طريقة AddGenericResolver من مكتبة ServiceModelex في Idesign.

ملاحظة: .NET 4.0 مطلوب كما يستخدم DataContractResolver

يمكنك العثور عليه على صفحة تنزيل Idesign.

كل ما كان علي فعله في حالتي هو إضافة السطر التالي من التعليمات البرمجية:

Client.AddGenericResolver( typeof ( K2Source ) );

آمل أن يساعد هذا شخص آخر هناك توفير بضع ساعات!

يمكنك العثور على مزيد من المعلومات في كتاب "Programming WCF Services: Mastering WCF و Azure AppFabric Service Bus" بواسطة Juval Lowy

هناك طريقة أخرى للقيام بذلك. بدلاً من استخدام "إضافة مرجع الخدمة" ، تقوم بترميز فئات الوكيل. إنه ترميز أكثر قليلاً في البداية ولكنه يمنحك حلًا أكثر استقرارًا وقوة. لقد وجدنا أن هذا يوفر لنا الوقت على المدى الطويل.

يرى: http://www.dnrtv.com/default.aspx؟shownum=122

ملاحظة: يعمل هذا فقط إذا كان لديك تحكم في كل من الخادم والعميل.

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