Azure cosmosdb 使用.Net SDK Microsoft.Azure.Cosmos查询Cosmos DB以获取不同派生类型的列表

Azure cosmosdb 使用.Net SDK Microsoft.Azure.Cosmos查询Cosmos DB以获取不同派生类型的列表,azure-cosmosdb,azure-cosmosdb-sqlapi,Azure Cosmosdb,Azure Cosmosdb Sqlapi,我们有一个接口和一个具有多个派生类型的基类 public interface IEvent { [JsonProperty("id")] public string Id { get; set; } string Type { get; } } public abstract class EventBase: IEvent { public string Id { get; set; } public abstract string

我们有一个接口和一个具有多个派生类型的基类

public interface IEvent
{
    [JsonProperty("id")]
    public string Id { get; set; }

    string Type { get; }
}

public abstract class EventBase: IEvent
{
    public string Id { get; set; }
    public abstract string Type { get; }
}

public class UserCreated : EventBase
{
    public override string Type { get; } = typeof(UserCreated).AssemblyQualifiedName;
}

public class UserUpdated : EventBase
{
    public override string Type { get; } = typeof(UserUpdated).AssemblyQualifiedName;
}
我们正在使用.Net SDK
Microsoft.Azure.Cosmos
的v3将这些不同派生类型的事件存储在Cosmos DB的同一容器中。然后,我们希望读取所有事件,并将它们反序列化为正确的类型

public class CosmosDbTests
{
    [Fact]
    public async Task TestFetchingDerivedTypes()
    {
        var endpoint = "";
        var authKey = "";
        var databaseId ="";
        var containerId="";

        var client = new CosmosClient(endpoint, authKey);

        var container = client.GetContainer(databaseId, containerId);

        await container.CreateItemAsync(new UserCreated{ Id = Guid.NewGuid().ToString() });
        await container.CreateItemAsync(new UserUpdated{ Id = Guid.NewGuid().ToString() });

        var queryable = container.GetItemLinqQueryable<IEvent>();

        var query = queryable.ToFeedIterator();
        var list = new List<IEvent>();

        while (query.HasMoreResults)
        {
            list.AddRange(await query.ReadNextAsync());
        }

        Assert.NotEmpty(list);
    }
}
公共类COSMOSDBTEST
{
[事实]
公共异步任务TestFetchingDerivedTypes()
{
var endpoint=“”;
var authKey=“”;
var databaseId=“”;
var containerId=“”;
var client=new-CosmosClient(端点,authKey);
var container=client.GetContainer(databaseId,containerId);
wait container.CreateItemAsync(新用户创建的{Id=Guid.NewGuid().ToString()});
wait container.CreateItemAsync(新用户已更新{Id=Guid.NewGuid().ToString()});
var queryable=container.GetItemLinqQueryable();
var query=queryable.ToFeedIterator();
var list=新列表();
while(query.HasMoreResults)
{
AddRange(wait query.ReadNextAsync());
}
Assert.NotEmpty(列表);
}
}
似乎没有任何选项告诉
GetItemLinqQueryable
如何处理类型。是否有其他方法或途径支持一个查询中的多个派生类型


如果有帮助的话,可以将事件放入某种包装器实体中,但是它们不允许作为序列化的sting存储在属性中。

Stephen的评论为我指明了正确的方向,在这个博客的帮助下,我最终得到了一个类似于以下示例的解决方案:我们有一个自定义的
CosmosSerializer
,它使用了一个自定义的
JsonConverter
读取类型属性

public interface IEvent
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("$type")]
    string Type { get; }
}

public abstract class EventBase: IEvent
{
    public string Id { get; set; }
    public string Type => GetType().AssemblyQualifiedName;
}

public class UserCreated : EventBase
{
}

public class UserUpdated : EventBase
{
}
public class EventJsonConverter : JsonConverter
{
    // This converter handles only deserialization, not serialization.
    public override bool CanRead => true;
    public override bool CanWrite => false;

    public override bool CanConvert(Type objectType)
    {
        // Only if the target type is the abstract base class
        return objectType == typeof(IEvent);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // First, just read the JSON as a JObject
        var obj = JObject.Load(reader);

        // Then look at the $type property:
        var typeName = obj["$type"]?.Value<string>();

        return typeName == null ? null : obj.ToObject(Type.GetType(typeName), serializer);

    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException("This converter handles only deserialization, not serialization.");
    }
}
EventJsonConverter
读取
Type
属性

public interface IEvent
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("$type")]
    string Type { get; }
}

public abstract class EventBase: IEvent
{
    public string Id { get; set; }
    public string Type => GetType().AssemblyQualifiedName;
}

public class UserCreated : EventBase
{
}

public class UserUpdated : EventBase
{
}
public class EventJsonConverter : JsonConverter
{
    // This converter handles only deserialization, not serialization.
    public override bool CanRead => true;
    public override bool CanWrite => false;

    public override bool CanConvert(Type objectType)
    {
        // Only if the target type is the abstract base class
        return objectType == typeof(IEvent);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // First, just read the JSON as a JObject
        var obj = JObject.Load(reader);

        // Then look at the $type property:
        var typeName = obj["$type"]?.Value<string>();

        return typeName == null ? null : obj.ToObject(Type.GetType(typeName), serializer);

    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException("This converter handles only deserialization, not serialization.");
    }
}
CosmosClient现在是使用我们自己的
NewtonSoftJSONCSOMassessiver
EventJsonConverter
创建的

public class CosmosDbTests
{
    [Fact]
    public async Task TestFetchingDerivedTypes()
    {
        var endpoint = "";
        var authKey = "";
        var databaseId ="";
        var containerId="";

        var client = new CosmosClient(endpoint, authKey, new CosmosClientOptions
        {
            Serializer = new NewtonsoftJsonCosmosSerializer(new JsonSerializerSettings
            {
                Converters = { new EventJsonConverter() }
            })
        });

        var container = client.GetContainer(databaseId, containerId);

        await container.CreateItemAsync(new UserCreated{ Id = Guid.NewGuid().ToString() });
        await container.CreateItemAsync(new UserUpdated{ Id = Guid.NewGuid().ToString() });

        var queryable = container.GetItemLinqQueryable<IEvent>();

        var query = queryable.ToFeedIterator();
        var list = new List<IEvent>();

        while (query.HasMoreResults)
        {
            list.AddRange(await query.ReadNextAsync());
        }

        Assert.NotEmpty(list);
    }
}
公共类COSMOSDBTEST
{
[事实]
公共异步任务TestFetchingDerivedTypes()
{
var endpoint=“”;
var authKey=“”;
var databaseId=“”;
var containerId=“”;
var client=new-CosmosClient(端点、authKey、new-CosmosClientOptions
{
Serializer=新的NewtonSoftJSONCSOMasserializer(新的JsonSerializerSettings
{
转换器={new EventJsonConverter()}
})
});
var container=client.GetContainer(databaseId,containerId);
wait container.CreateItemAsync(新用户创建的{Id=Guid.NewGuid().ToString()});
wait container.CreateItemAsync(新用户已更新{Id=Guid.NewGuid().ToString()});
var queryable=container.GetItemLinqQueryable();
var query=queryable.ToFeedIterator();
var list=新列表();
while(query.HasMoreResults)
{
AddRange(wait query.ReadNextAsync());
}
Assert.NotEmpty(列表);
}
}

您是否使用定制的
宇宙采集器
?没有,但您的问题帮助我找到了这个博客,帮助我找到了解决方案。