Predicate Expression, WCF, LLBLGen 4.0

Posts   
1  /  2
 
    
chand
User
Posts: 72
Joined: 05-Aug-2007
# Posted on: 28-Dec-2013 04:50:31   

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.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 28-Dec-2013 06:57:36   

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?

David Elizondo | LLBLGen Support Team
chand
User
Posts: 72
Joined: 05-Aug-2007
# Posted on: 28-Dec-2013 07:21:01   

EntityHelper is our indirection to create TypedList EntityField.

On the server we are passing this expression to TypedList's IRelationPredicateBucket which was obtained from the method GetRelationInfo() of typedlist instance

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 28-Dec-2013 07:29:53   

Please try my suggestion above, I think that you don't need to manually create those fields anymore. If you insist, please show us the code inside EntityFactoryHelper.

David Elizondo | LLBLGen Support Team
chand
User
Posts: 72
Joined: 05-Aug-2007
# Posted on: 28-Dec-2013 07:40:13   

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.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 28-Dec-2013 11:10:24   

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?

Frans Bouma | Lead developer LLBLGen Pro
chand
User
Posts: 72
Joined: 05-Aug-2007
# Posted on: 28-Dec-2013 15:02:15   

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?

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 30-Dec-2013 06:47:55   

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.

David Elizondo | LLBLGen Support Team
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 30-Dec-2013 10:43:46   

Thanks, David for this detective work sunglasses . 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.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 30-Dec-2013 12:19:52   

Fixed. See attached dll. (v4.1)

Attachments
Filename File size Added on Approval
SD.LLBLGen.Pro.ORMSupportClasses.zip 419,163 30-Dec-2013 12:20.00 Approved
Frans Bouma | Lead developer LLBLGen Pro
chand
User
Posts: 72
Joined: 05-Aug-2007
# Posted on: 30-Dec-2013 14:18:52   

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);

}

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 30-Dec-2013 18:07:44   

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.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 30-Dec-2013 20:37:57   

Let's try again! simple_smile 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.

Attachments
Filename File size Added on Approval
SD.LLBLGen.Pro.ORMSupportClasses.zip 419,614 30-Dec-2013 20:38.03 Approved
Frans Bouma | Lead developer LLBLGen Pro
chand
User
Posts: 72
Joined: 05-Aug-2007
# Posted on: 30-Dec-2013 23:43:13   

That works! Thanks

chand
User
Posts: 72
Joined: 05-Aug-2007
# Posted on: 03-Jan-2014 19:57:41   

There is one more problem around the serialization of EntityField2 in a predicate expression.

ExpressionToApply property is not reaching over the wire in 4.0, which was working fine in 3.5.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 04-Jan-2014 06:26:15   

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;
}
David Elizondo | LLBLGen Support Team
chand
User
Posts: 72
Joined: 05-Aug-2007
# Posted on: 07-Jan-2014 16:19:37   

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)}));

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 07-Jan-2014 19:42:57   

Could you please answer this question?

Are you using v4.0 or v4.1? The build attached by Frans is for v4.1.

chand
User
Posts: 72
Joined: 05-Aug-2007
# Posted on: 08-Jan-2014 14:30:20   

We are using 4.1 with the attached Frans's change

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 08-Jan-2014 15:22:58   

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);
}


Frans Bouma | Lead developer LLBLGen Pro
chand
User
Posts: 72
Joined: 05-Aug-2007
# Posted on: 08-Jan-2014 17:17:19   

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>

chand
User
Posts: 72
Joined: 05-Aug-2007
# Posted on: 09-Jan-2014 15:58:55   

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()
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 10-Jan-2014 11:15:20   

(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.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 10-Jan-2014 12:14:23   

I can reproduce it with netTCP binding in wcf. that's something at least simple_smile

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 simple_smile

(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.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 10-Jan-2014 14:22:15   

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.

Attachments
Filename File size Added on Approval
SD.LLBLGen.Pro.ORMSupportClasses.zip 419,725 10-Jan-2014 14:22.24 Approved
Frans Bouma | Lead developer LLBLGen Pro
1  /  2