سؤال

تطبيق عملائي / خادم المستخدم يستخدم WCF للاتصال، والذي كان رائعا. ومع ذلك، فإن قرص واحد للهندسة المعمارية الحالية هو أنه يجب علي استخدام تكوين نوع معروف لبعض أنواع نقل. أنا أستخدم حانة داخلية / آلية فرعية وهذا الشرط أمر لا مفر منه.

المشكلة هي أنه من السهل أن تنسى إضافة النوع المعروف، وإذا قمت بذلك، فشل WCF بصمت مع أدلة قليلة حول ما يحدث.

في طلبي، أعرف مجموعة الأنواع التي سيتم إرسالها. أود إجراء التكوين برمجيا، بدلا من الإعلان من خلال App.config الملف الذي يحتوي حاليا على شيء مثل هذا:

<system.runtime.serialization>
  <dataContractSerializer>
    <declaredTypes>
      <add type="MyProject.MyParent, MyProjectAssembly">
        <knownType type="MyProject.MyChild1, MyProjectAssembly"/>
        <knownType type="MyProject.MyChild2, MyProjectAssembly"/>
        <knownType type="MyProject.MyChild3, MyProjectAssembly"/>
        <knownType type="MyProject.MyChild4, MyProjectAssembly"/>
        <knownType type="MyProject.MyChild5, MyProjectAssembly"/>
      </add>
    </declaredTypes>
  </dataContractSerializer>
</system.runtime.serialization>

بدلا من ذلك، أود أن أفعل شيئا مثل هذا:

foreach (Type type in _transmittedTypes)
{
    // How would I write this method?
    AddKnownType(typeof(MyParent), type);
}

يمكن لشخص يرجى توضيح كيف قد أفعل هذا؟

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

هذا هو أساسا سؤال حول WCF API، وليس سؤال أسلوب.

تحرير 2. صفحة MSDN هذه تنص الصفحة:

يمكنك أيضا إضافة أنواع إلى ReadOnlyCollection، والتي تم الوصول إليها من خلال الخاصية المعروفة من DataConctractSerializer.

لسوء الحظ، هذا كل ما تقوله ولا يصنع الكثير من الناحية الرهيبة التي تعطى أن المعروفة هي خاصية Readonly، وقيمة الخاصية هي ReadOnlyCollection.

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

المحلول

يضيف [ServiceKnownType] لك [ServiceContract] واجهه المستخدم:

[ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))]

ثم قم بإنشاء فئة تسمى KnownTypesProvider:

internal static class KnownTypesProvider
{
    public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
    {
         // collect and pass back the list of known types
    }
}

ثم يمكنك العودة أيا كانت أنواع تحتاجها.

نصائح أخرى

هناك 2 طرق إضافية لحل مشكلتك:

1. استخدم NearningTyPeattribute (سلسلة):

[DataContract]
[KnownType("GetKnownTypes")]
public abstract class MyParent
{
    static IEnumerable<Type> GetKnownTypes()
    {
        return new Type[] { typeof(MyChild1), typeof(MyChild2), typeof(MyChild3) };
    }
}

II. استخدم منشئ Dataconctractractializer.

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | 
                AttributeTargets.Interface)]
public class MyHierarchyKnownTypeAttribute : Attribute, IOperationBehavior, IServiceBehavior, IContractBehavior
{
    private void IOperationBehavior.AddBindingParameters(
            OperationDescription description, 
            BindingParameterCollection parameters)
    {
    }

    void IOperationBehavior.ApplyClientBehavior(
            OperationDescription description, 
            ClientOperation proxy)
    {
        ReplaceDataContractSerializerOperationBehavior(description);
    }

    private void IOperationBehavior.ApplyDispatchBehavior(
            OperationDescription description, 
            DispatchOperation dispatch)
    {
        ReplaceDataContractSerializerOperationBehavior(description);
    }

    private void IOperationBehavior.Validate(OperationDescription description)
    {
    }

    private void IServiceBehavior.AddBindingParameters(
          ServiceDescription serviceDescription,
          ServiceHostBase serviceHostBase,
          System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,
          BindingParameterCollection bindingParameters)
    {
        ReplaceDataContractSerializerOperationBehavior(serviceDescription);
    }

    private void IServiceBehavior.ApplyDispatchBehavior(
            ServiceDescription serviceDescription, 
            ServiceHostBase serviceHostBase)
    {
        ReplaceDataContractSerializerOperationBehavior(serviceDescription);
    }

    private void IServiceBehavior.Validate(ServiceDescription serviceDescription,
            ServiceHostBase serviceHostBase)
    {
    }

    private void IContractBehavior.AddBindingParameters(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint, 
            BindingParameterCollection bindingParameters)
    {
    }

    private void IContractBehavior.ApplyClientBehavior(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        ReplaceDataContractSerializerOperationBehavior(contractDescription);
    }

    private void IContractBehavior.ApplyDispatchBehavior(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
    {
        ReplaceDataContractSerializerOperationBehavior(contractDescription);
    }

    private void IContractBehavior.Validate(ContractDescription contractDescription,
            ServiceEndpoint endpoint)
    {
    }    

    private static void ReplaceDataContractSerializerOperationBehavior(
            ServiceDescription description)
    {
        foreach (var endpoint in description.Endpoints)
        {
            ReplaceDataContractSerializerOperationBehavior(endpoint);
        }
    }

    private static void ReplaceDataContractSerializerOperationBehavior(
            ContractDescription description)
    {
        foreach (var operation in description.Operations)
        {
            ReplaceDataContractSerializerOperationBehavior(operation);
        }
    }

    private static void ReplaceDataContractSerializerOperationBehavior(
            ServiceEndpoint endpoint)
    {
        // ignore mex
        if (endpoint.Contract.ContractType == typeof(IMetadataExchange))
        {
            return;
        }
        ReplaceDataContractSerializerOperationBehavior(endpoint.Contract);
    }

    private static void ReplaceDataContractSerializerOperationBehavior(
            OperationDescription description)
    {
        var behavior = 
         description.Behaviors.Find<DataContractSerializerOperationBehavior>();
        if (behavior != null)
        {
            description.Behaviors.Remove(behavior);
            description.Behaviors.Add(
                new ShapeDataContractSerializerOperationBehavior(description));
        }
    }

    public class ShapeDataContractSerializerOperationBehavior 
            : DataContractSerializerOperationBehavior
    {
        public ShapeDataContractSerializerOperationBehavior(
                OperationDescription description)
            : base(description) { }

        public override XmlObjectSerializer CreateSerializer(Type type, 
                string name, string ns, IList<Type> knownTypes)
        {
            var shapeKnownTypes = 
                new List<Type> { typeof(Circle), typeof(Square) };
            return new DataContractSerializer(type, name, ns, shapeKnownTypes);
        }

        public override XmlObjectSerializer CreateSerializer(Type type, 
                XmlDictionaryString name, XmlDictionaryString ns, 
                IList<Type> knownTypes)
        {
            //All magic here!
            var knownTypes = 
                new List<Type> { typeof(MyChild1), typeof(MyChild2), typeof(MyChild3) };
            return new DataContractSerializer(type, name, ns, knownTypes);
        }
    }
}

[ServiceContract()]
[MyHierarchyKnownTypeAttribute]
public interface IService {...}

ملاحظة: يجب عليك استخدام هذه السمة على كلا الجانبين: جانب العميل وجانب الخدمة!

كنت بحاجة للقيام بذلك للسماح الميراث للعمل بشكل صحيح. لم أكن أرغب في الحفاظ على قائمة الأنواع المشتقة.

 [KnownType("GetKnownTypes")]
 public abstract class BaseOperationResponse
 {

    public static Type[] GetKnownTypes()
    {
        Type thisType = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType;
        return thisType.Assembly.GetTypes().Where(t => t.IsSubclassOf(thisType)).ToArray();
    }

أعلم أن السطر الأول من الوظيفة مبالغ فيه، لكنه يعني فقط أن أصبه في أي فئة أساسية دون تعديل.

ويب .كونفيغ

<applicationSettings>
<HostProcess.Properties.Settings>
<setting name="KnowTypes" serializeAs="Xml">
<value>
 <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <string>a.AOrder,a</string>
   <string>b.BOrder,b</string>
   <string>c.COrder,c</string>
 </ArrayOfString>
</value>
</setting>
</HostProcess.Properties.Settings>

static class Helper
{
    public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
    {
        System.Collections.Generic.List<System.Type> knownTypes =
        new System.Collections.Generic.List<System.Type>();
        // Add any types to include here.
        Properties.Settings.Default.KnowTypes.Cast<string>().ToList().ForEach(type =>
            {
                knownTypes.Add(Type.GetType(type));
            });

        return knownTypes;
    }
}


[ServiceContract]
[ServiceKnownType("GetKnownTypes", typeof(Helper))]
public interface IOrderProcessor
{
    [OperationContract]
    string ProcessOrder(Order order);
}

النظام هو الطبقة الأساسية مجردة


[DataContract]
public abstract class Order
{
    public Order()
    {
        OrderDate = DateTime.Now;
    }
    [DataMember]
    public string OrderID { get; set; }
    [DataMember]
    public DateTime OrderDate { get; set; }
    [DataMember]
    public string FirstName { get; set; }
    [DataMember]
    public string LastName { get; set; }
}

مبالغة قليلا، ولكنها تعمل ونوع من الدليل في المستقبل

var knownTypes =
    AppDomain.CurrentDomain
    .GetAssemblies()
    .Where(a =>
    {
        var companyAttribute = a.GetCustomAttribute<AssemblyCompanyAttribute>();
        if (companyAttribute == null) return false;
        return companyAttribute.Company.ToLower().Contains("[YOUR COMPANY NAME]");
    })
    .SelectMany(a => a.GetTypes()).Where(t => t.IsSerializable && !t.IsGenericTypeDefinition);

var serializer = new DataContractSerializer(type, knownTypes);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top