I've been looking for the answer to the following problem for a few days, so far without any luck, any help is much appreciated.
Disclaimer: WCF and EntityFramework are new-ish concepts to me, but .NET, c#, web services, Object Relational Mapping concepts certainly aren't.
I inherited an application with a Client app using a WCF service to retrieve and save a "Matter" object which contains a 1-to-many relationship with "CaseNotesLog" objects.
The Client can successfully retreive certain Matters with 0, 1 or many child CaseNotesLog objects, it can also successfully save back Matters with added, updated or deleted CaseNotesLog objects.
So far so good, however there appears to be a set of Matters, with child CaseNotesLog objects that can be read successfully but cannot be saved even if they have not been altered at all.
Essentially it looks like the deserialization process on the WCF when saving the Matter object looses the child CaseNotesLog entries.
To clarify:
- Read
- a Matter record has 1 CaseNotesLog child record in the Database,
- this is successfully translated into a EntityFramework Matter object with a child CaseNotesLog object on the Server
- this is successfully serialized/deserialized across the WCF Service and correctly populates a Matter object with a child CaseNotesLog on the client side.
- Save
- the client-side Matter object with child CaseNotesLog object "leaves" the client app.
- this is serialized to pass across the WCF Service back to the server.
- looking at the Trace logging this serialization contains the child CaseNotesLog.
- this object is deserialized on the server side
- By adding trace statements into the Setter code in the entityframework Matter object I can observe the CaseNotesLog children being correctly placed into the parent Matter.
- BUT as soon as the Getter functionality is called the collection is empty
Its as if the RelationshipManager collection is being reset somewhere else.
The end result is that a Matter object with a child CaseNotesLog object leaves my code on the client-side but although it looks like it is serializing/deserializing correctly by the time I get to check the state of the object on the server-side the child records have gone.
To check I added some code to the entityframework class to keep an independent count of how large the child collection should be. It gets set to 1 during the Set operation when the CaseNotesLog collection is set, and it remains at 1 when the CaseNotesLog collection is retrieved during the Get, even though the collection has been emptied.
To add to the confusion *this does not apply to all Matters, some seem to work others don't *this does not seem to apply to all types of child objects other sets seem to work.
The WCF Service, amongst other methods, implements the following:
[ServiceContract]
public interface IMattersService
{
...
[OperationContract]
Matter ReadMatter(int matterID);
...
[OperationContract]
void SaveMatter(Matter matter);
...
}
}
It is implemented as follows:
public class MattersService : IMattersService
{
...
public Matter ReadMatter(int matterID)
{
using (var repo = new MatterRepository())
{
try
{
return repo.ReadMatter(matterID);
}
catch (Exception ex)
{
Logger.Write(ex, "Error", 0);
throw;
}
}
}
...
public void SaveMatter(Matter matter)
{
Trace.WriteLine(matter.CaseNotesLogs.Count);
using (var repo = new MatterRepository())
{
try
{
repo.SaveMatter(matter);
}
catch (Exception ex)
{
Logger.Write(ex, "Error", 0);
throw;
}
}
}
...
}
The Matter is an EntityFramework generated class:
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmEntityTypeAttribute(NamespaceName="EDDSolutionsLtd.Services.Data", Name="Matter")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class Matter : EntityObject
{
...
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[XmlIgnoreAttribute()]
[SoapIgnoreAttribute()]
[DataMemberAttribute()]
[EdmRelationshipNavigationPropertyAttribute("EDDSolutionsLtd.Services.Data", "MatterCaseNotesLog", "CaseNotesLog")]
public EntityCollection<CaseNotesLog> CaseNotesLogs
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<CaseNotesLog>("EDDSolutionsLtd.Services.Data.MatterCaseNotesLog", "CaseNotesLog");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<CaseNotesLog>("EDDSolutionsLtd.Services.Data.MatterCaseNotesLog", "CaseNotesLog", value);
}
}
}
...
}
The mangled version I added some logging to:
public int CheckCaseNotesLogsCount { get; set; }
public EntityCollection<CaseNotesLog> CheckCaseNotesLogs { get; set; }
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[XmlIgnoreAttribute()]
[SoapIgnoreAttribute()]
[DataMemberAttribute()]
[EdmRelationshipNavigationPropertyAttribute("EDDSolutionsLtd.Services.Data", "MatterCaseNotesLog", "CaseNotesLog")]
public EntityCollection<CaseNotesLog> CaseNotesLogs
{
get
{
if (CheckCaseNotesLogs != null)
{
Trace.WriteLine("GET ACTUAL CheckCaseNotesLogs[" + CheckCaseNotesLogs.Count + "]");
}
Trace.WriteLine("GET ACTUAL CheckCaseNotesLogsCount[" + CheckCaseNotesLogsCount + "]");
StackTrace st = new StackTrace();
int i = ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<CaseNotesLog>("EDDSolutionsLtd.Services.Data.MatterCaseNotesLog", "CaseNotesLog").Count;
Trace.WriteLine("GET ACTUAL value[" + i + "]");
Trace.WriteLine(st.ToString());
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<CaseNotesLog>("EDDSolutionsLtd.Services.Data.MatterCaseNotesLog", "CaseNotesLog");
}
set
{
if ((value != null))
{
CheckCaseNotesLogs = value;
CheckCaseNotesLogsCount = value.Count;
Trace.WriteLine("SET ACTUAL value[" + value.Count + "]");
((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<CaseNotesLog>("EDDSolutionsLtd.Services.Data.MatterCaseNotesLog", "CaseNotesLog", value);
Trace.WriteLine("SET ACTUAL Property value[" + this.CaseNotesLogs.Count + "]");
}
else
{
Trace.WriteLine("SET NULL Value");
}
}
}
This is a snippet of the Soap message received by the Wcf Service showing the child CaseNotesLog record is present.
<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
<System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
<EventID>0</EventID>
<Type>3</Type>
<SubType Name="Information">0</SubType>
<Level>8</Level>
<TimeCreated SystemTime="2011-05-19T22:49:34.7968750Z" />
<Source Name="System.ServiceModel.MessageLogging" />
<Correlation ActivityID="{fa25d214-04e9-4b16-8ae8-3de7b9a12bc6}" />
<Execution ProcessName="WebDev.WebServer40" ProcessID="2332" ThreadID="13" />
<Channel />
<Computer>EDD-MERCURY</Computer>
</System>
<ApplicationData>
<TraceData>
<DataItem>
<MessageLogTraceRecord Time="2011-05-19T23:49:34.7812500+01:00"
Source="ServiceLevelReceiveRequest"
Type="System.ServiceModel.Channels.BufferedMessage"
xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
<HttpRequest>
<Method>POST</Method>
<QueryString></QueryString>
<WebHeaders>
<Content-Length>142631</Content-Length>
<Content-Type>application/soap+xml;
charset=utf-8</Content-Type>
<Expect>100-continue</Expect>
<Host>localhost:58080</Host>
<VsDebuggerCausalityData>
uIDPo7kHl/h9zQNAkghvTvB5/u8AAAAAuoDOged1MUm+UmudC0H6u3k/74R6jUpIn/o2sS4KNxYACQAA</VsDebuggerCausalityData>
</WebHeaders>
</HttpRequest>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">
http://tempuri.org/IMattersService/SaveMatter</a:Action>
<a:MessageID>
urn:uuid:f1d751a7-1012-4268-b7da-87a12cf6d14f</a:MessageI开发者_如何学PythonD>
<ActivityId CorrelationId="4f53460f-8880-4b6e-aa66-557fa90d4ae3"
xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">
fa25d214-04e9-4b16-8ae8-3de7b9a12bc6</ActivityId>
<a:ReplyTo>
<a:Address>
http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">
http://localhost:58080/MattersService.svc</a:To>
</s:Header>
<s:Body>
<SaveMatter xmlns="http://tempuri.org/">
<matter z:Id="i1"
xmlns:b="http://schemas.datacontract.org/2004/07/EDDSolutionsLtd.Services.Data"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<EntityKey z:Id="i2"
xmlns="http://schemas.datacontract.org/2004/07/System.Data.Objects.DataClasses"
xmlns:c="http://schemas.datacontract.org/2004/07/System.Data">
<c:EntityContainerName>
EDDSolutionsLtdEntities</c:EntityContainerName>
<c:EntityKeyValues>
<c:EntityKeyMember>
<c:Key>ID</c:Key>
<c:Value i:type="d:int"
xmlns:d="http://www.w3.org/2001/XMLSchema">
180</c:Value>
</c:EntityKeyMember>
</c:EntityKeyValues>
<c:EntitySetName>Matters</c:EntitySetName>
</EntityKey>
<b:AllRecordsReceivedDate i:nil="true">
</b:AllRecordsReceivedDate>
<b:BeginClaimDate i:nil="true">
</b:BeginClaimDate>
...
<b:CaseEmailLogs></b:CaseEmailLogs>
<b:CaseFileLogs></b:CaseFileLogs>
<b:CaseNotesLogs>
<b:CaseNotesLog z:Id="i3">
<EntityKey z:Id="i4"
xmlns="http://schemas.datacontract.org/2004/07/System.Data.Objects.DataClasses"
xmlns:c="http://schemas.datacontract.org/2004/07/System.Data">
<c:EntityContainerName>
EDDSolutionsLtdEntities</c:EntityContainerName>
<c:EntityKeyValues>
<c:EntityKeyMember>
<c:Key>ID</c:Key>
<c:Value i:type="d:int"
xmlns:d="http://www.w3.org/2001/XMLSchema">
281</c:Value>
</c:EntityKeyMember>
</c:EntityKeyValues>
<c:EntitySetName>
CaseNotesLogs</c:EntitySetName>
</EntityKey>
<b:ActivityDate>
2011-05-18T00:00:00</b:ActivityDate>
<b:Body i:nil="true"></b:Body>
<b:Comment>3</b:Comment>
<b:ContactID>608</b:ContactID>
<b:CreatedBy>mminns</b:CreatedBy>
<b:CreatedOn>
2011-05-19T15:25:03.923</b:CreatedOn>
<b:ID>281</b:ID>
<b:MatterId>180</b:MatterId>
<b:ModifiedBy>mminns</b:ModifiedBy>
<b:ModifiedOn>
2011-05-19T15:25:03.923</b:ModifiedOn>
<b:OriginalContactID>0</b:OriginalContactID>
<b:Outcome>Unknown</b:Outcome>
<b:Subject>1</b:Subject>
</b:CaseNotesLog>
</b:CaseNotesLogs>
...
</matter>
</SaveMatter>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>
</DataItem>
</TraceData>
</ApplicationData>
</E2ETraceEvent>
Can you mark this attribute for the data contract : System.Runtime.Serialization.DataContractAttribute(IsReference=true)]
精彩评论