[CSHARP-1757] LINQ support for types with custom serialization Created: 26/Aug/16  Updated: 31/Mar/22

Status: Backlog
Project: C# Driver
Component/s: Linq
Affects Version/s: 2.2.4, 2.3.0-beta1
Fix Version/s: None

Type: New Feature Priority: Major - P3
Reporter: Vyacheslav Stroy Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: question
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Duplicate

 Description   

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.



 Comments   
Comment by bhaarsma [ 08/Jan/19 ]

Have you found a way to make it work?

Generated at Wed Feb 07 21:40:36 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.