Return base class from Specification in Ardalis Specification for CosmosDB

138 views Asked by At

In CosmosDB collection I have 2 types of entities which inherit from one base class. I want to return base class from specification and be able to convert that base class object to concrete ones. I don't use EF, I use CosmosDB SDK. I want to have something like that:

class BaseClass
{
   string Id;
}


class ClassA : BaseClass
{
string Status;
}

class ClassB : BaseClass
{
string Amount;
}

public class MyScpec : Specification<BaseClass>
{
    public MyScpec () 

{

Query.Select(x =>
(x is ClassA
             ? new ClassA { Id = x.Id, Status = x.Status }

: new ClassB { Id = x.Id, Amount = x.Amount}
));

}
}

And further in code I want to be able to do like that:

var list = _repository.List(new MySpec());

foreach(var item in list)
{
    if(item is ClassA)    { ...}

}

I tried to return BaseClass:

Query.Select(new BaseClass(){...})

But then I can't convert it to child classes.

Also, I tried to do something like in EF, when we can configure entities

builder.HasBaseType<BaseClass>();

But there is no such a possibility in CosmosDB SDK as far as I know.

1

There are 1 answers

0
Nikita Romashov On

I found a soolution. You need to have a custom json converter

public abstract class AbstractJsonConverter<T> : JsonConverter
{
    protected abstract T? Create(Type objectType, JObject jObject);

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }
        
        var jObject = JObject.Load(reader);
        if (jObject.Type == JTokenType.Null)
        {
            return null;
        }
        
        var target = Create(objectType, jObject);
        serializer.Populate(jObject.CreateReader(), target!);

        return target;
    }

    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

public class DataModelConverter : AbstractJsonConverter<DataModelBase>
{
    protected override DataModelBase Create(Type objectType, JObject jObject)
    {
        switch (jObject[nameof(DataModel.id)]!.Value<string>())
        {
            case var id when id!.StartsWith("typeOne"):
                return new DataModelOne();
            case var id when id!.StartsWith("typeTwo"):
                return new DataModelTwo();
            default:
                throw new ArgumentException();
        }
    }
}