- Home
- LLBLGen Pro
- Bugs & Issues
Predicate Expression, WCF, LLBLGen 4.0
Joined: 05-Aug-2007
We have a bit of code written years back that allows client code to send where clauses across the wire.
On the serverside queries are happening on TypedLists.
Client code can create the predicate expression and send it as a method parameter via WCF call.
Here is the client code
var where = new PredicateExpression(MyTypedListFields.ClientName == "test"); var result = ClientService.Proxy.FetchClient(where);
As there is no way to get the EntityField2 from TypedListIndex, we wrote a generator to generate those fields
Ex:
class MyTypedListFields { public static EntityField2 ClientName { get { return EntityFactoryHelper.Instance.GetEntityField(TypedListType.MyTypedListFields, (int) MyTypedListFieldIndex.ClientName); } } }
In version 4.1 the above query works fine on server, but when called from client we are getting the following error.
System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail] : An exception was caught during the execution of a retrieval query: The multi-part identifier error
Query: Where clause is not using the alias.
SELECT DISTINCT TOP(2)
[LPA_K1].[Name] AS [ClientName]
FROM [ourdb].[schema].[MyEntity] [LPA_K1]
WHERE ( [ourdb].[schema].[MyEntity].Name = 'test') ORDER BY [LPA_K1].[Name] ASC
Update 1:
It looks like in version 4.0 ObjectAlias value of predicate expressions is not coming over the wire.
Update 2:
It works if I construct the predicate expression differently
new FieldCompareValuePredicate(MyTypedListFields.ClientName, null, ComparisonOperator.Equal, "test", MyTypedListFields.ClientName.ObjectAlias));
But this a log of code change for us.
Joined: 28-Nov-2005
Why didn't you use TypedListFields.ClientName?
var where = new PredicateExpression(TypedListFields.ClientName == "test");
Are you using the filter directly on the server or are doing something else like injecting the predicate expression to another filter?
(Edit) What is EntityFactoryHelper?
Joined: 05-Aug-2007
This generator was written about 6 years back.
I am sure I am missing something here.
I see field index are being generated <My>TypedListFieldIndex
But not <My>TypedListFields
I guess this is the reason why we wrote generator for this code.
If you point me in the right direction, I am glad to get rid of the genarator. EntityHelper is using reflection to create an instance of typedlist. That is it.
I also tried using EntityFields (typed list is made of that entity). Because the object alias is not there EntityFields predicate also failed.
Joined: 17-Aug-2003
Just for my understanding: say the typedlist T contains one entity, E. This entity is aliased as E. Your helper now creates fields of E which have ObjectAlias set as "E"?
You're sending the predicate over the wire using xml?
Joined: 05-Aug-2007
Answer is 'Yes' to both the questions.
Following call even through works is too long. There are too many places to change.
new PredicateExpression(new FieldCompareValuePredicate(MyTypedListFields.ClientName, null, ComparisonOperator.Equal, "test", MyTypedListFields.ClientName.ObjectAlias)));
Where as the following call is much shorter.
new PredicateExpression(MyTypedListFields.ClientName == "test")
I am wondering why both are resulting in different behaviors over the wire?
Joined: 28-Nov-2005
I finally understand your problem about the alias. I reproduced it using the latest v4.1 build. These are my tests:
[TestMethod]
public void SerializeObjectAliasOnFieldCoreUsingLLBLGen()
{
var predicate = new PredicateExpression();
predicate.Add(CustomerFields.CompanyName.SetObjectAlias("E") == "A");
// succeeds
Assert.AreEqual("E", ((FieldCompareValuePredicate)predicate[0].Contents).FieldCore.ObjectAlias);
var predicateAsXmlString = SerializeUsingLLBLGen(predicate);
var predicateResult = DeserializeUsingLLBLGen(predicateAsXmlString);
// fails
Assert.AreEqual("E", ((FieldCompareValuePredicate)predicateResult[0].Contents).FieldCore.ObjectAlias);
}
[TestMethod]
public void SerializeObjectAliasOnContentsUsingLLBLGen()
{
var predicate = new PredicateExpression();
predicate.Add(new FieldCompareValuePredicate(CustomerFields.CompanyName, null, ComparisonOperator.Equal, "A", "E"));
// succeeds
Assert.AreEqual("E", ((FieldCompareValuePredicate)predicate[0].Contents).ObjectAlias);
var predicateAsXmlString = SerializeUsingLLBLGen(predicate);
var predicateResult = DeserializeUsingLLBLGen(predicateAsXmlString);
// succeeds
Assert.AreEqual("E", ((FieldCompareValuePredicate)predicateResult[0].Contents).ObjectAlias);
}
static string SerializeUsingLLBLGen(IPredicateExpression predicate)
{
var sb = new StringBuilder();
var writer = System.Xml.XmlWriter.Create(sb);
((IPredicateExpression)predicate).WriteXml(writer);
writer.Flush();
var result = sb.ToString();
writer.Close();
return result;
}
static PredicateExpression DeserializeUsingLLBLGen(string predicateAsString)
{
var sr = new StringReader(predicateAsString);
var reader = System.Xml.XmlReader.Create(sr);
var predicateToReturn = new PredicateExpression();
((IPredicateExpression)predicateToReturn).ReadXml(reader);
return predicateToReturn;
}
The first test passes the OjectAlias to the field object. The second test passes the ObjectAlias to the Predicate itself. Apparently, in the first case, the ObjectAlias is not serialized, maybe because of the following code: (EntityFieldCore.cs:467)
protected virtual void WriteDefinitionAsXml(XmlWriter writer)
{
writer.WriteStartElement("Field"); // <Field>
writer.WriteOptionalAttributeString("ActAsDerivedField", _actAsDerivedTableField, XmlConvert.ToString(_actAsDerivedTableField), false);
if(_fieldInfo != null)
{
_fieldInfo.WriteXml(writer);
}
if(_dynamicUsageInfo!=null)
{
_dynamicUsageInfo.WriteXml(writer);
}
WriteAdditionalElementsToXml(writer);
writer.WriteEndElement(); // </Field>
}
The ObjectAlias (which is part of the EntityFieldCore) is not included in the xml. We will investigate further into this.
Joined: 17-Aug-2003
Thanks, David for this detective work . Bug, will fix.
(edit) to elaborate why we didn't catch this sooner: the xml serialization tests we have focus on having the object alias specified in the predicate itself, as that's what is usually done. The ObjectAlias was missed in the xml serialization as it was assumed it was part of the dynamicinfo inside a field, which it isn't. For normal entity serialization this isn't used, as that's all about data serialization. We missed this spot and will fix it.
Joined: 17-Aug-2003
Fixed. See attached dll. (v4.1)
Filename | File size | Added on | Approval |
---|---|---|---|
SD.LLBLGen.Pro.ORMSupportClasses.zip | 419,163 | 30-Dec-2013 12:20.00 | Approved |
Joined: 05-Aug-2007
This fix didn't work. I am still having the same problem. ObjectAlias is empty string on the server.
I used DataContractSerializer
Try the following test
[TestMethod] public void SerializeObjectAliasOnFieldCoreUsingLLBLGen() { var predicate = new PredicateExpression(); predicate.Add(CustomerFields.CompanyName.SetObjectAlias("E") == "A");
// succeeds
Assert.AreEqual("E", ((FieldCompareValuePredicate)predicate[0].Contents).FieldCore.ObjectAlias);
var serializer = new DataContractSerializer(predicate.GetType(), new List<Type>(){typeof(PredicateExpressionElement),
typeof(FieldCompareValuePredicate), typeof(EntityField2)});
var memoryStream = new MemoryStream();
serializer.WriteObject(memoryStream, predicate);
memoryStream.Position = 0;
var predicateResult= (IPredicateExpression)serializer.ReadObject(memoryStream);
// fails
Assert.AreEqual("E", ((FieldCompareValuePredicate)predicateResult[0].Contents).FieldCore.ObjectAlias);
}
Joined: 17-Aug-2003
Ok, we identified a problem: the main idea was that serializing the predicates etc. wasn't such a good idea, as it promotes coupling between client and server: the predicate should be created on the server. To serialize a unit of work, we needed serialization of predicates to xml (as they can be specified as filters) however we control that from the unit of work. They use the same methods / interface as IXmlSerializable, but don't explicitly implement that interface.
This means that if you serialize them directly, it doesn't work. The code is there, but as the explicit interface isn't on the class, it won't be used directly. Passing a writer, using our helper methods, works OK, and that's also how it's used in our framework and how we tested it, however if you want to serialize a predicate individually, it's not going to work without using our helper class.
I'll add the IXmlSerializable interface and see whether that breaks anything. It shouldn't as the code on the objects work with reader/writers, exactly how IXmlSerializable works.
Joined: 17-Aug-2003
Let's try again! IXmlSerializable is now explicitly defined on the types (predicates, expression, sortclause etc.) and the objects now properly serialize/deserialize to/from xml using xmlserializer/netdatacontractserializer and datacontractserializer. See attachment.
Filename | File size | Added on | Approval |
---|---|---|---|
SD.LLBLGen.Pro.ORMSupportClasses.zip | 419,614 | 30-Dec-2013 20:38.03 | Approved |
Joined: 28-Nov-2005
Please elaborate more on your issue. Are you using v4.0 or v4.1? The build attached by Frans is for v4.1. I cannot reproduce your error over here with the new fixed build:
[TestMethod]
public void SerializeExpressionToApplyOnFieldCoreUsingDCS()
{
var predicate = new PredicateExpression();
var expression = new DbFunctionCall("LEFT", new object[]{ CustomerFields.CompanyName});
predicate.Add(CustomerFields.CompanyName.SetExpression(expression) == "A");
Assert.AreEqual("LEFT", ((DbFunctionCall)((FieldCompareValuePredicate)predicate[0].Contents).FieldCore.ExpressionToApply).FunctionName);
var predicateAsXmlString = SerializeUsingDCS(predicate);
var predicateResult = DeserializeUsingDCS(predicateAsXmlString);
Assert.AreEqual("LEFT", ((DbFunctionCall)((FieldCompareValuePredicate)predicateResult[0].Contents).FieldCore.ExpressionToApply).FunctionName);
}
static string SerializeUsingDCS(IPredicateExpression predicate)
{
Type[] knownTypes = new Type[]
{
typeof(PredicateExpression),
typeof(PredicateExpressionElement),
typeof(PredicateExpressionElementType),
typeof(EntityField2),
typeof(FieldCompareValuePredicate)
};
DataContractSerializer dcs = new DataContractSerializer(typeof(PredicateExpression), knownTypes);
var sb = new StringBuilder();
var writer = System.Xml.XmlWriter.Create(sb);
dcs.WriteObject(writer, predicate);
writer.Flush();
var result = sb.ToString();
writer.Close();
return result;
}
static PredicateExpression DeserializeUsingDCS(string predicateAsString)
{
Type[] knownTypes = new Type[]
{
typeof(PredicateExpression),
typeof(PredicateExpressionElement),
typeof(PredicateExpressionElementType),
typeof(EntityField2),
typeof(FieldCompareValuePredicate)
};
DataContractSerializer dcs = new DataContractSerializer(typeof(PredicateExpression), knownTypes);
var sr = new StringReader(predicateAsString);
var reader = System.Xml.XmlReader.Create(sr);
var predicateToReturn = (PredicateExpression)dcs.ReadObject(reader);
return predicateToReturn;
}
Joined: 05-Aug-2007
It took a while for me to trace the problem.
ExpressionToApply property is null when I use net.tcp binding
ServiceCode:
[ServiceContract] public interface ITest { [OperationContract] bool TestExpression(SearchRequest expression); } public class Service : ITest { public bool TestExpression(SearchRequest request) { return ((FieldCompareValuePredicate)request.Predicate[0].Contents).FieldCore.ExpressionToApply != null; } }
[DataContract(Name = "SearchCriteria")] [KnownType(typeof (PredicateExpression))] [KnownType(typeof(PredicateExpressionElement))] [KnownType(typeof(FieldCompareValuePredicate))] [KnownType(typeof(EntityField2))] public class SearchRequest { [DataMember] public PredicateExpression Predicate { get; set; } }
Client Code:
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(GetBinding(), new EndpointAddress(baseAddress)); ITest proxy = factory.CreateChannel(); var field = CustomerFields.JoinDate; field.ExpressionToApply = new DbFunctionCall("YEAR", new object[] {CustomerFields.JoinDate}); Console.WriteLine(proxy.TestExpression(new SearchRequest {Predicate = new PredicateExpression(field == 2014)}));
Joined: 17-Aug-2003
Ok but what serializer is used at runtime? IXmlSerializable using serializers will serialize the expression to xml... as David's test shows. Reviewing the code, there's no way it can't serialize the expression to xml: same with aggregate. If you set the aggregate function on the field, it too should come across, it's in the same object as the expression inside a field object.
We need a proper repro case for this so we can see what exactly is called. I have to say up front that we can't obey every serializer there is, as there are only 2 interfaces to implement: if they don't obey these interfaces (which they don't it seems) we can't do much else. Field's IXmlSerializable interface calls WriteDefinitionAsXml which writes the expression as well.
Can you reproduce it in a local test with a given serializer? (so without the overhead of wcf)
(also could you try to add DbFunctionCall as a known type?)
(edit) this succeeds:
[Test]
public void SerializeObjectAliasAndExpressionOnFieldCore()
{
var predicate = new PredicateExpression();
predicate.Add(AddressFields.AddressLine1.SetObjectAlias("E").SetExpression(new DbFunctionCall("YEAR", new object[] {CustomerFields.ModifiedDate})) == "A");
Assert.AreEqual("E", ((FieldCompareValuePredicate)predicate[0].Contents).FieldCore.ObjectAlias);
var serializer = new System.Runtime.Serialization.DataContractSerializer(predicate.GetType(), new List<Type>(){typeof(PredicateExpressionElement),
typeof(FieldCompareValuePredicate), typeof(EntityField2)});
var memoryStream = new System.IO.MemoryStream();
serializer.WriteObject(memoryStream, predicate);
memoryStream.Position = 0;
var predicateResult = (IPredicateExpression)serializer.ReadObject(memoryStream);
var deserializedPredicateField = ((FieldCompareValuePredicate)predicateResult[0].Contents).FieldCore;
Assert.AreEqual("E", deserializedPredicateField.ObjectAlias);
Assert.IsNotNull(deserializedPredicateField.ExpressionToApply);
Assert.AreEqual("YEAR", ((DbFunctionCall)deserializedPredicateField.ExpressionToApply).FunctionName);
Assert.AreEqual("ModifiedDate", ((IEntityFieldCore)((DbFunctionCall)deserializedPredicateField.ExpressionToApply).FunctionParameters[0]).Name);
}
Joined: 05-Aug-2007
I cannot reproduce this with local serialization tests!!
I tried this with both NetDataContract and DataContract Serializers. I tried with attributing knowtypes.
Whenever I use BasicHttpBinding everything is fine. Problem is only when I use net.tcp binding.
Here is the SOAP request on the wire for both bindings.
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/ITest/TestExpression</a:Action>
<a:MessageID>urn:uuid:ff1b84f2-5bf6-4a53-8611-566bc20bb79b</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPozFCOXcE9w1Hqhtmj2eMqscAAAAAOnWSqXf1a0ea3/4eUwy3dOmSclKpK2NMgPxTdOVUmPQACQAA</VsDebuggerCausalityData>
</s:Header>
<s:Body>
<TestExpression xmlns="http://tempuri.org/">
<SearchRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" z:Type="WorkSpaceService.SearchRequest" z:Assembly="WorkSpaceService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/WorkSpaceService">
<Predicate xmlns:d5p1="http://schemas.datacontract.org/2004/07/SD.LLBLGen.Pro.ORMSupportClasses" z:Id="2">
<d5p1:_instanceType>5</d5p1:_instanceType>
<d5p1:_negate>false</d5p1:_negate>
<d5p1:_objectAlias z:Id="3" />
<d5p1:_selfServicing>false</d5p1:_selfServicing>
<d5p1:_predicates xmlns:d6p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" z:Id="4">
<d6p1:_items z:Id="5" z:Size="4">
<d6p1:anyType z:Id="6" z:Type="SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpressionElement" z:Assembly="SD.LLBLGen.Pro.ORMSupportClasses, Version=4.1.0.0, Culture=neutral, PublicKeyToken=ca73b74ba4e3ff27">
<d5p1:_contents z:Id="7" z:Type="SD.LLBLGen.Pro.ORMSupportClasses.FieldCompareValuePredicate" z:Assembly="SD.LLBLGen.Pro.ORMSupportClasses, Version=4.1.0.0, Culture=neutral, PublicKeyToken=ca73b74ba4e3ff27">
<d5p1:_instanceType>3</d5p1:_instanceType>
<d5p1:_negate>false</d5p1:_negate>
<d5p1:_objectAlias z:Ref="3" i:nil="true" />
<d5p1:_selfServicing>false</d5p1:_selfServicing>
<d5p1:_caseSensitiveCollation>false</d5p1:_caseSensitiveCollation>
<d5p1:_comparisonOperator>Equal</d5p1:_comparisonOperator>
<d5p1:_field z:Id="8" z:Type="SD.LLBLGen.Pro.ORMSupportClasses.EntityField2" z:Assembly="SD.LLBLGen.Pro.ORMSupportClasses, Version=4.1.0.0, Culture=neutral, PublicKeyToken=ca73b74ba4e3ff27">
<Field>
<FieldInfo Name="JoinDate" ContainingObjectName="CustomerEntity" DataType="System.DateTime" FieldIndex="1" />
<DynamicInfo>
<Expression Type="SD.LLBLGen.Pro.ORMSupportClasses.DbFunctionCall" FunctionName="YEAR">
<Parameters>
<Parameter Type="0">
<Field>
<FieldInfo Name="JoinDate" ContainingObjectName="CustomerEntity" DataType="System.DateTime" FieldIndex="1" />
</Field>
</Parameter>
</Parameters>
</Expression>
</DynamicInfo>
</Field>
</d5p1:_field>
<d5p1:_persistenceInfo i:nil="true" />
<d5p1:_value z:Id="9" z:Type="System.Int32" z:Assembly="0">2014</d5p1:_value>
</d5p1:_contents>
<d5p1:_type>Predicate</d5p1:_type>
</d6p1:anyType>
<d6p1:anyType i:nil="true" />
<d6p1:anyType i:nil="true" />
<d6p1:anyType i:nil="true" />
</d6p1:_items>
<d6p1:_size>1</d6p1:_size>
<d6p1:_version>1</d6p1:_version>
</d5p1:_predicates>
</Predicate>
</SearchRequest>
</TestExpression>
</s:Body>
</s:Envelope>
Joined: 05-Aug-2007
Frans,
This doesn't explain why this behavior is changed in LLBLGen 4.1 version, while it was fine in 3.5. What is different about EntityFieldCore2 between these two versions that would cause these serialization issues only in 4.1?
May be that the internal class DynamicFieldInfo is failing to deserialize when used with net.tcp?
As you implemented IXMLSerializable on these types I tried to manually switch to XMLSerializationFormat instead of default DataContractSerialization
http://msdn.microsoft.com/en-us/library/ms733901(v=vs.110).aspx
[ServiceContract]
[b] [XmlSerializerFormat][/b]
public interface ITest
{
[OperationContract]
bool TestExpression(PredicateExpression expression);
}
Now I can't even start the service. I am getting the following errors.
There was an error reflecting 'expression'.
at System.Xml.Serialization.XmlReflectionImporter.ImportMembersMapping(XmlReflectionMember[] xmlReflectionMembers, String ns, Boolean hasWrapperElement, Boolean rpc, Boolean openModel, RecursionLimiter limiter)
at System.Xml.Serialization.XmlReflectionImporter.ImportMembersMapping(String elementName, String ns, XmlReflectionMember[] members, Boolean hasWrapperElement, Boolean rpc, Boolean openModel, XmlMappingAccess access)
at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector. XmlSerializerImporter.ImportMembersMapping(XmlName elementName, String ns, XmlReflectionMember[] members, Boolean hasWrapperElement, Boolean rpc, Boolean isEncoded, String mappingKey)
at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector. OperationReflector.ImportMembersMapping(String elementName, String ns, XmlReflectionMember[] members, Boolean hasWrapperElement, Boolean rpc, String mappingKey)
at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector. OperationReflector.LoadBodyMapping(MessageDescription message, String mappingKey, MessagePartDescriptionCollection& rpcEncodedTypedMessageBodyParts)
at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector. OperationReflector.CreateMessageInfo(MessageDescription message, String key)
at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector. OperationReflector.EnsureMessageInfos()
at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector.EnsureMessageInfos()
at System.ServiceModel.Description.XmlSerializerOperationBehavior.CreateFormatter()
at System.ServiceModel.Description.XmlSerializerOperationBehavior.System.ServiceModel. Description.IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch)
at System.ServiceModel.Description.DispatcherBuilder.BindOperations(ContractDescription contract, ClientRuntime proxy, DispatchRuntime dispatch)
at System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost)
at System.ServiceModel.ServiceHostBase.InitializeRuntime()
at System.ServiceModel.ServiceHostBase.OnBeginOpen()
at System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open()
at WorkSpaceService.LLBLService.Test() in c:\Work\Bugs\UpdateBug\WorkSpaceService\Program.cs:line 217
at WorkSpaceService.Program.Main(String[] args) in c:\Work\Bugs\UpdateBug\WorkSpaceService\Program.cs:line 28
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Joined: 17-Aug-2003
(edit) this post is outdated. It still contains answers to your questions, but the issue has been identified and solved, see further down.
original text:
chand wrote:
Frans,
This doesn't explain why this behavior is changed in LLBLGen 4.1 version, while it was fine in 3.5. What is different about EntityFieldCore2 between these two versions that would cause these serialization issues only in 4.1?
May be that the internal class DynamicFieldInfo is failing to deserialize when used with net.tcp?
In 3.5 the predicate wasn't xml serializable, so that it serialized to xml at all was luck: the reflection based system used by some serializers works in some situations and your code lucked out there. In v4 we added proper xml serialization to the predicates etc. so they would serialize and deserialize properly in all situations. We missed a spot, which was found by you earlier in this thread but that should be covered by now. Looking at the soap xml in your previous post, the predicate is serialized fine with the dbfunction call properly there. The problem I think is that the xml doesn't look like it's serialized by our code but by a reflection based serializer as it inserts types into the xml, which we don't do (as we know the types). Odd thing is though, half way through the predicate xml, it suddenly seems to switch to our code for the field: there it shows no xml namespace usage, just plain xml. It's odd that the predicate itself isn't serialized with our serializer, as it clearly implements IXmlSerializable, at least with the fix I attached earlier in this thread.
We have a WCF example, which uses nettcp binding for the non-IIS hosting version. I will try to see whether I can reproduce it there and also check whether the xml serializable code is called, and if so, when...
As you implemented IXMLSerializable on these types I tried to manually switch to XMLSerializationFormat instead of default DataContractSerialization
http://msdn.microsoft.com/en-us/library/ms733901(v=vs.110).aspx
[ServiceContract] [b] [XmlSerializerFormat][/b] public interface ITest { [OperationContract] bool TestExpression(PredicateExpression expression); } Now I can't even start the service. I am getting the following errors. There was an error reflecting 'expression'. at System.Xml.Serialization.XmlReflectionImporter.ImportMembersMapping(XmlReflectionMember[] xmlReflectionMembers, String ns, Boolean hasWrapperElement, Boolean rpc, Boolean openModel, RecursionLimiter limiter) at System.Xml.Serialization.XmlReflectionImporter.ImportMembersMapping(String elementName, String ns, XmlReflectionMember[] members, Boolean hasWrapperElement, Boolean rpc, Boolean openModel, XmlMappingAccess access) at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector. XmlSerializerImporter.ImportMembersMapping(XmlName elementName, String ns, XmlReflectionMember[] members, Boolean hasWrapperElement, Boolean rpc, Boolean isEncoded, String mappingKey) at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector. OperationReflector.ImportMembersMapping(String elementName, String ns, XmlReflectionMember[] members, Boolean hasWrapperElement, Boolean rpc, String mappingKey) at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector. OperationReflector.LoadBodyMapping(MessageDescription message, String mappingKey, MessagePartDescriptionCollection& rpcEncodedTypedMessageBodyParts) at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector. OperationReflector.CreateMessageInfo(MessageDescription message, String key) at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector. OperationReflector.EnsureMessageInfos() at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector.EnsureMessageInfos() at System.ServiceModel.Description.XmlSerializerOperationBehavior.CreateFormatter() at System.ServiceModel.Description.XmlSerializerOperationBehavior.System.ServiceModel. Description.IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch) at System.ServiceModel.Description.DispatcherBuilder.BindOperations(ContractDescription contract, ClientRuntime proxy, DispatchRuntime dispatch) at System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost) at System.ServiceModel.ServiceHostBase.InitializeRuntime() at System.ServiceModel.ServiceHostBase.OnBeginOpen() at System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout) at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) at System.ServiceModel.Channels.CommunicationObject.Open() at WorkSpaceService.LLBLService.Test() in c:\Work\Bugs\UpdateBug\WorkSpaceService\Program.cs:line 217 at WorkSpaceService.Program.Main(String[] args) in c:\Work\Bugs\UpdateBug\WorkSpaceService\Program.cs:line 28 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()
Ok. Please double-check whether you're using the runtime I attached earlier in this thread. It should return a date of december 30 when you read RuntimeLibraryVersion.Build at runtime. It shouldn't use any reflection, the interfaces are there.
(edit) I think I understand why the xml shows reflection based xml at first, because the SearchRequest isn't implementing IXmlSerializable, so it will be serialized using reflection. Also the predicate property isn't typed (the object in it is of a derived type) so it has to specify which type it is. I'll try to send the predicate over directly first, to see whether that works.
Joined: 17-Aug-2003
I can reproduce it with netTCP binding in wcf. that's something at least
Service interface:
[OperationContract]
IEntityCollection2 GetOrdersBasedOnPredicate(FieldCompareValuePredicate filter);
Service method impl:
public IEntityCollection2 GetOrdersBasedOnPredicate(FieldCompareValuePredicate filter)
{
var toReturn = new EntityCollection(new OrderEntityFactory());
if(filter.FieldCore.ExpressionToApply == null)
{
throw new ArgumentNullException("ExpressionToApply");
}
using(var adapter = new DataAccessAdapter())
{
var qf = new QueryFactory();
var q = qf.Order.Where(filter);
adapter.FetchQuery(q, toReturn);
}
return toReturn;
}
client:
private void _testPredicateButton_Click(object sender, EventArgs e)
{
var predicate = new FieldCompareValuePredicate(new EntityField2("OrderYear", new DbFunctionCall("YEAR", new object[] { OrderFields.OrderDate }), typeof(int)),
null, ComparisonOperator.GreaterThan, 1997);
var results = _dalService.GetOrdersBasedOnPredicate(predicate);
Debug.Assert(results.Count != 263);
}
I hacked it into the wcf example, when using the non-iis hosted wcf service, which uses nettcp binding. I'll now see whether I can find any reason why this is as I can now debug it.
(edit) serializing seems OK, everything traverses through the proper code, everything is serialized. Will now look at the deserializing code at the other end.
(edit) deserializing runs into a problem it seems. The FieldInfo is deserialized, but the xml apparently doesn't contain more xml nodes below the field element, while the serializer wrote them. very odd...
(edit) The problem was that <FieldInfo> is ending up in the XML as <FieldInfo ...></FieldInfo>. Which is seen as a non-empty element by the XmlReader, even though there's no content inside the xml and all we do is write a start element, some atttributes and an end element. It's unclear when this ends up as <element attribute1=".."/> or <element attribute1="..."></element> but the difference is that the latter requires a different approach when reading nodes as it's not seen as an empty element, the former is. This is a problem as the reader code we have thinks it has arrived at its own xml subtree. As FieldInfo is a subtree (albeit without nodes), we need to read it as a subtree. When I add the code to do so (using the extension methods we added for xml reading/writing, they're in XmlExtensionMethods.cs in Miscellaneous), it gets across, though there are then some other problems which are related to your other thread, I see the same issue popping up now. So I'll now look at that and see whether that requires a change as well. Stay tuned, we're almost there
(edit) the issue the code runs into is this: http://social.msdn.microsoft.com/Forums/vstudio/en-US/b709f2fc-52a4-4702-802b-e79c41188867/ixmlserializable-with-empty-elements-in-wcf?forum=wcf
which is exactly what we see here too: the xml has an endtag for FieldInfo, while normally it's not having that. The docs on XmlWriter clearly state it should never write an end element when there are no sub elements (one should use WriteFullEndElement for that instead), however they appear here.
After reviewing all code, the only place where we don't use a subtree reader is in FieldInfo, so we added a simple check whether it's indeed having a dangling end element (reader.IsEmptyElement is then false), and if so, read if away, as there's none expected.
After that everything worked except the dbfunctioncall with a field, as that's caused by the fact the deserializer code there had a small bug that it didn't pre-sized the persistence info array for field parameters. After fixing that, it all worked (and unittests still pass).
We'll now review your other thread, and go back to the code to review it again to see where we missed a persistence info array size specification in deserialization code.
Joined: 17-Aug-2003
Fixed. See attached dll. We reviewed where empty elements were written to xml, and where no subtree reader was used. That was only in FieldInfo, which fixes your first problem. The other problem (of your other thread) manifests only in dbfunction call if fields are used as parameters and in entityrelation. Groupbycollection works OK. We fixed this issue as well. So both the issue in this thread and the other thread with relationpredicatebucket should now work OK. We didn't ran into this ourselves as this isn't a recommended scenario: sending predicates over the wire outside a unit of work (where they're used as filters for selective updates) is not recommended. However as you have code that does this already, it should of course be nice if you can keep it. I hope the attached dll will fix the problems.
Filename | File size | Added on | Approval |
---|---|---|---|
SD.LLBLGen.Pro.ORMSupportClasses.zip | 419,725 | 10-Jan-2014 14:22.24 | Approved |