3

質問票オブジェクトをクライアントに返そうとすると、次のエラーが発生します。提案されているように、データコントラクトにKnowType [typeof(...)]を追加しましたが、それでも機能しません。シリアライザーに不明なタイプがわからないので、EFモデルにあるすべてのクラスを投入しました。誰かが助けることができますか?ありがとう。

これがサービス契約です

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using QuestionnaireWcfServiceApp.Models;

namespace QuestionnaireWcfService
{
    [ServiceContract]
    public interface IQuestionnaireService
    {
        [OperationContract]
        QuestionnaireContract GetQuestionnaire(string questionnaireName);

        [OperationContract]
        QuestionChain LoadQuestion(int questionnaireID, int? questionID, int? userResponse);
    }

    [DataContract]
    [KnownType(typeof(Questionnaire))]
    [KnownType(typeof(Question))]
    [KnownType(typeof(Choice))]
    [KnownType(typeof(Decision))]
    [KnownType(typeof(QuestionFlow))]
    public class QuestionChain
    {
        [DataMember]
        public Question Question { get; set; }

        [DataMember]
        public int? Decision {get;set;}
    }

    [DataContract]
    [KnownType(typeof(Questionnaire))]
    [KnownType(typeof(Question))]
    [KnownType(typeof(Choice))]
    [KnownType(typeof(Decision))]
    [KnownType(typeof(QuestionFlow))]
    public class QuestionnaireContract
    {
        [DataMember]
        public Questionnaire Questionnaire { get; set; }
    }
}

これがサービスです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using QuestionnaireWcfServiceApp.Models;

namespace QuestionnaireWcfService
{
    public class QuestionnaireService : IQuestionnaireService
    {
        QuestionnaireWcfServiceApp.Models.QuestionnaireEntities db = new QuestionnaireEntities();

        public QuestionnaireContract GetQuestionnaire(string questionnaireName)
        {
            QuestionnaireContract questionnaireContract = new QuestionnaireContract();
            if (!string.IsNullOrEmpty(questionnaireName))
            {
                Questionnaire thisQuestionnaire = (from q in db.Questionnaires where q.Name.Equals(questionnaireName) select q).FirstOrDefault();
                if (thisQuestionnaire == null)
                    throw new ArgumentNullException("Questionnaire ID is not found.");
                else
                {
                    questionnaireContract.Questionnaire = thisQuestionnaire;
                    return questionnaireContract;
                }
            }
            else
                throw new ArgumentException("Questionnaire name is not specified.");
        }

        public QuestionChain LoadQuestion(int questionnaireID, int? currentQuestionID, int? userResponse)
        {
            QuestionChain qc = new QuestionChain();
            QuestionFlow thisFlow = null;
            Question nextQuestion = null;           
            Questionnaire thisQuestionnaire = (from q in db.Questionnaires where q.QuestionnaireId == questionnaireID select q).FirstOrDefault();

            if (thisQuestionnaire == null)
                throw new ArgumentNullException("Questionnaire ID is not found");   //InvalidOperationException;

            if (currentQuestionID.HasValue)
            {
                //QuestionID should never be changed after setup.  Change the QuestionText around the QuestionID
                Question thisQuestion = thisQuestionnaire.Questions.Where(q => q.PKey.Equals(currentQuestionID)).FirstOrDefault();
                if (thisQuestion == null)
                    throw new ArgumentNullException("Question ID is not found");
                else
                {
                    if (userResponse.HasValue)
                    {
                        thisFlow = thisQuestion.QuestionFlows.First(f => f.QuestionId.Equals(currentQuestionID) && f.ChoiceId.Equals(userResponse));
                        if (thisFlow.Question1 != null)
                        {
                            nextQuestion = thisFlow.Question1;
                            qc.Question = nextQuestion;
                        }
                        else
                        {
                            qc.Question = null;
                            qc.Decision = thisFlow.Decision.Value;
                        }
                    }
                    else
                    {
                        //can't happen. when reaches here, a userResponse must not be null
                    }
                }
            }           
            else
            {
                //default to question 1
                nextQuestion = thisQuestionnaire.Questions.First(q => q.QuestionId.Equals(1));
                if (nextQuestion == null)
                    throw new ArgumentNullException("Question ID");
                else
                    qc.Question = nextQuestion;
            }
            return qc;
        }
    }
}

これは、Windowsアプリケーションログの例外です。

Exception: System.ServiceModel.CommunicationException: There was an error while trying to serialize parameter http://tempuri.org/:GetQuestionnaireResult. The InnerException message was 'Type System.Data.Entity.DynamicProxies.Questionnaire_EF00247BEFB9F733C947A4C3E57FD12709E91510AC0DA534D137ED75FCCAC342' 
with data contract name 'Questionnaire_EF00247BEFB9F733C947A4C3E57FD12709E91510AC0DA534D137ED75FCCAC342:
http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' 
is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'.  Please see InnerException for more details. ---> System.Runtime.Serialization.SerializationException: Type 'System.Data.Entity.DynamicProxies.Questionnaire_EF00247BEFB9F733C947A4C3E57FD12709E91510AC0DA534D137ED75FCCAC342' with data contract name 'Questionnaire_EF00247BEFB9F733C947A4C3E57FD12709E91510AC0DA534D137ED75FCCAC342:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
           at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
           at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType)
           at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
           at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
           at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
           at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)
           at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph)
           --- End of inner exception stack trace ---
           at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph)
           at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameter(XmlDictionaryWriter writer, PartInfo part, Object graph)
           at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest)
           at System.ServiceModel.Dispatcher.OperationFormatter.SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, Object[] parameters, Object returnValue, Boolean isRequest)
           at System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
           at System.ServiceModel.Channels.BodyWriterMessage.OnBodyToString(XmlDictionaryWriter writer)
           at System.ServiceModel.Channels.Message.ToString(XmlDictionaryWriter writer)
           at System.ServiceModel.Diagnostics.MessageLogTraceRecord.WriteTo(XmlWriter writer)
           at System.ServiceModel.Diagnostics.MessageLogger.LogInternal(MessageLogTraceRecord record)
           at System.ServiceModel.Diagnostics.MessageLogger.LogMessageImpl(Message& message, XmlReader reader, MessageLoggingSource source)
           at System.ServiceModel.Diagnostics.MessageLogger.LogMessage(Message& message, XmlReader reader, MessageLoggingSource source)
         Process Name: WebDev.WebServer40
         Process ID: 11620

        Event Xml:
        <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
          <System>
            <Provider Name="System.ServiceModel 4.0.0.0" />
            <EventID Qualifiers="49154">5</EventID>
            <Level>2</Level>
            <Task>7</Task>
            <Keywords>0x80000000000000</Keywords>
            <TimeCreated SystemTime="2012-10-18T07:32:11.000000000Z" />
            <EventRecordID>36499</EventRecordID>
            <Channel>Application</Channel>
            <Computer>Jon-PC</Computer>
            <Security UserID="S-1-5-21-334737869-2079735299-2176000493-1000" />
          </System>
          <EventData>
            <Data>System.ServiceModel.CommunicationException: There was an error while trying to serialize parameter http://tempuri.org/:GetQuestionnaireResult. The InnerException message was 'Type 'System.Data.Entity.DynamicProxies.Questionnaire_EF00247BEFB9F733C947A4C3E57FD12709E91510AC0DA534D137ED75FCCAC342' with data contract name 'Questionnaire_EF00247BEFB9F733C947A4C3E57FD12709E91510AC0DA534D137ED75FCCAC342:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'.  Please see InnerException for more details. ---&gt; System.Runtime.Serialization.SerializationException: Type 'System.Data.Entity.DynamicProxies.Questionnaire_EF00247BEFB9F733C947A4C3E57FD12709E91510AC0DA534D137ED75FCCAC342' with data contract name 'Questionnaire_EF00247BEFB9F733C947A4C3E57FD12709E91510AC0DA534D137ED75FCCAC342:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
           at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
           at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType)
           at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
           at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
           at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
           at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)
           at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph)
           --- End of inner exception stack trace ---
           at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph)
           at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameter(XmlDictionaryWriter writer, PartInfo part, Object graph)
           at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest)
           at System.ServiceModel.Dispatcher.OperationFormatter.SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, Object[] parameters, Object returnValue, Boolean isRequest)
           at System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
           at System.ServiceModel.Channels.BodyWriterMessage.OnBodyToString(XmlDictionaryWriter writer)
           at System.ServiceModel.Channels.Message.ToString(XmlDictionaryWriter writer)
           at System.ServiceModel.Diagnostics.MessageLogTraceRecord.WriteTo(XmlWriter writer)
           at System.ServiceModel.Diagnostics.MessageLogger.LogInternal(MessageLogTraceRecord record)
           at System.ServiceModel.Diagnostics.MessageLogger.LogMessageImpl(Message&amp; message, XmlReader reader, MessageLoggingSource source)
           at System.ServiceModel.Diagnostics.MessageLogger.LogMessage(Message&amp; message, XmlReader reader, MessageLoggingSource source)</Data>
            <Data>WebDev.WebServer40</Data>
            <Data>11620</Data>
          </EventData>
        </Event>
4

2 に答える 2

4

WCFで使用されるすべてのタイプは、データコントラクトであるか、[Serializable]としてマークされている必要があります。これには、すでにWCFデータコントラクトに含まれているすべてのオブジェクトが含まれます。能力がある場合は、[Serializable]タグをクラスに追加するか、WCF固有のタグをクラスに追加する必要があります。この冗談ではありませんが、KnownTypesにTypeを追加しても、WCFがそれがシリアル化可能であることを認識しているわけではありません。

これらのオプションのどちらも使用できない場合は、渡す値を含む「プロキシ」オブジェクトを作成し、それらをターゲットオブジェクトとの間で変換することをお勧めします。クレイジーに聞こえますが、それはそれが行く方法です...

追加された例:

WCFは、System.Runtime.Serialization名前空間に含まれているシリアライザーを利用して、データをさまざまな形式(XML、SOAP、JSON、バイナリ)にシリアル化および逆シリアル化します。これが機能するためには、シリアル化されるオブジェクトは、ある種のシリアル化可能なタイプ(属性でマークされている)である必要があります。データオブジェクトの組み込みのWCF属性は、次のようになります。

//This is marked with the DataContract attribute, which is WCF specific
[DataContract]
public class Foo
{
    //The DataMember attribute is also WCF specific and specifies what data
    // is included in the serialization.  Any properties (or variables) must be
    // accessable, as in not read only.
    [DataMember]
    public string Property1{get;set;}

    //This variable will be serialized as well, even though it is private.  This
    // works great if you have a readonly property but still need to pass the data
    [DataMember]
    private int Id = 0;


    //This does not have a DataMember attribute and will not be serialized
    private string var1;
}

WCFは、 Serializableとしてマークされたクラス(DataTableやDataSetなど)を利用することもできます。

//This is marked with the Serializable attribute.  All public and private
// fields are automatically serialized (unless there is a containing object
// that is not serializable then you get a SerializationException
[Serializable]
public class Bar
{
    //gets serialized
    public string Property1{get;set;}

    //gets serialized
    private string var1;
}

「プロキシ」オブジェクトを作成するという考え方は、サービス参照を追加したときにVisual Studioによって生成されるものと同じですが、逆になっているだけです。シリアル化できず、特定のプロパティを持つクラス「Foo2」があるとします。

public class Foo2
{
    public string Property1{get;set;}

    public string Property2{get;set;}

    public int Property3{get;set;}
}

サービスとの間でプロパティをやり取りできるようにするプロキシクラスを(ほぼすべての方法で)作成できます。

[DataContract]
public class Foo2Proxy
{
    [DataMember]
    public string Property1{get;set;}

    [DataMember]
    public string Property2{get;set;}

    [DataMember]
    public int Property3{get;set;}

    public Foo2Proxy()
    {
    }

    public Foo2Proxy(Foo2 foo)
    {
        this.Property1 = foo.Property1;
        this.Property2 = foo.Property2;
        this.Property3 = foo.Property3;
    }

    public static Foo2 Create(Foo2Proxy fProxy)
    {
        var foo = new Foo2();
        foo.Property1 = fProxy.Property1;
        foo.Property2 = fProxy.Property2;
        foo.Property3 = fProxy.Property3;
        return foo;
    }
}

これを行う方法については、おそらく10万の異なる方法や意見があると思いますが、これは当時の私にとって有効だった可能性のほんの一例です。CodeProjectのこの記事を見ると、私がここで何を撮影していたかがわかります。

特定のケースでは、EFによって作成されたQuestionおよびDecisionタイプの「プロキシ」オブジェクト(またはそれを呼び出したいラッパーオブジェクト)を作成する必要がある場合があります。これらのオブジェクト用に生成されたコードに入り、上記のように属性を追加できます。追加の考慮事項の1つは、それらが別のクラス(または抽象クラス)から派生している場合、その基本クラスもシリアル化可能またはDataContractとしてマークする必要があるということです。

于 2012-10-18T17:11:11.380 に答える
2

ここでの問題は、 QuestionnaireEntitiesを使用してデータベースにクエリを実行したときに取得するオブジェクトがプロキシタイプであり、Questionnaire(またはユーザーが定義した他のタイプ)タイプではないことです。

返されたオブジェクトの.GetType()を呼び出すことをテストできます。

プロキシは、遅延読み込みをサポートするために作成されます。プロパティにアクセスしようとしたときにのみデータが読み込まれます。

WCFサービスからそれらを返す場合は、プロキシの作成を停止するようにEntityFrameworkに指示する必要があります。

次のコードを書くことでそれを行うことができます:

db.Configuration.ProxyCreationEnabled = false;

注:私が知る限り、プロキシをオフにすると、遅延読み込みも効果的にオフになります。Entity Frameworkでの私の経験から、遅延読み込みまたは読み込みなしの2つのオプションがあります。

したがって、遅延読み込みを無効にすると、すべてのナビゲーションプロパティがnullになります。EFにこれらのプロパティの値もロードさせるには、クエリでInclude()メソッドを使用する必要があります。

于 2012-10-18T18:11:08.747 に答える