Uploaded image for project: 'C# Driver'
  1. C# Driver
  2. CSHARP-1757

LINQ support for types with custom serialization

    • Type: Icon: New Feature New Feature
    • Resolution: Unresolved
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: 2.2.4, 2.3.0-beta1
    • Component/s: Linq
    • Labels:

      I am trying to implement suitable reference container for objects, located in separate collections. For example,

      //some interface to specify persistent objects with Id
      public interface IPersistentObject
      {
          long Id { get; }
      }
      
      //and two entities mapped to separate collections
      public class Sample: IPersistentObject
      {
          public long Id { get; set; }
      }
      
      public class SampleWithReference: IPersistentObject
      {
          public long Id { get; set; }
          public Ref<Sample> Parent { get; set; }
      }
      

      Using described entities.

      var sample = new Sample() {Id: 32412132};
      var sampleWithRef = new SampleWithReference() {Id: 5635634};
      sampleWithRef.Parent.Set(sample);
      //and save both objects
      

      It results in the following DB record for SampleWithReference object:

      {
        "Id": 5635634,
        "Parent": 32412132
      }
      

      The reference container itself:

      //here is the reference container
      public class Ref<T> : IPersistentObject, IEquatable<long>, IEqualityComparer<long> where T : IPersistentObject, new()
      {
          public long Id { get; private set; }
      
          private T _Object;
      
          public T Get()
          {
              return _Object;
          }
      
          public void Set(T value)
          {
              _Object = value;
              Id = value == null ? 0L : value.Id;
          }
          //IEquatable and IEqualityComparer memebers implementation here
      }
      

      And serializer for it:

      public class RefSerializer<T> : SerializerBase<Ref<T>> where T : IPersistentObject, new()
      {        
      	public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, Ref<T> value)
      	{
      		long v = value;
      		context.Writer.WriteInt64(v);
      	}
      	public override Ref<T> Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
      	{
      		var id = context.Reader.ReadInt64();
      		return new Ref<T>(id);
      	}
      }
      

      Everything works great except LINQ doesn't support join on the Ref<> properties.

      This code can't be compiled because of type mismatch. equals operator strictly requires the same arguments type on both sides of expression. Implementation of IEquatable<long> and implicit conversion operators doesn't help.

      from r in DB.Query<SampleWithReference>()
      join s in DB.Query<Sample>() on r.Parent equals s.Id
      select s;
      

      The following expression fails because it searches nonexistent path Parent.Id. Meanwhile the db document looks like this: {"Id": 5635634, "Parent": 32412132}.

      from r in DB.Query<SampleWithReference>()
      join s in DB.Query<Sample>() on r.Parent.Id equals s.Id
      select s;
      

      What I've tried:

      • IBsonDocumentSerializer implementation can't help.
      • LINQ extension methods are useless because all translator logic is hidden with internal modifier inside driver assembly.

      I'm looking for a way to make joins work respecting the provided serializer. It's vitally important for my project. I couldn't find a suitable way to hook LINQ logic.

      Any advice will be much appreciated.

            Assignee:
            Unassigned Unassigned
            Reporter:
            kreig Vyacheslav Stroy
            Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: