首页 > 解决方案 > 在 ef core 中实现动态数据模型的最佳方法

问题描述

我有:Ef Core 5Postgresql模型

public class Workitem
{
    public Guid Id { get; set; }
    public string Caption { get; set; }
    
    public List<Field> Fields { get; set; }
}
public struct Field
{
    public Guid Id { get; set; }
    public string Caption { get; set; }
    public FieldValueType ValueType { get; set; }
    public object Value { get; set; }

    public override bool Equals(object obj)
        => ReferenceEquals(this, obj) || obj is Field other && this.Value == other.Value;

    public override int GetHashCode()
        => Value.GetHashCode();
}
public enum FieldValueType
{
    ValueAsString,
    ValueAsDate,
    ValueAsDatetime,
    ValueAsInt,
    ValueAsDouble,
    ValueAsBool,
    ValueAsDictionary
}

我想实现在 Workitem 中存储一组动态字段的能力,而每个字段可以有不同类型的值。我需要一个可以方便地在客户端(应用程序)和数据库服务器端(转换为 SQL 时)使用的解决方案。

db.Workitems.Where(wi => wi.Fields.Any(f => 
       (f.ValueType == FieldValueType.ValueAsInt && f.Value >= 2)
    || (f.ValueType == FieldValueType.ValueAsString == "abc"))
    .ToList();

或者

new List<Workitem>(){ new Workitem() { Fields = new List<Fields>() { new Field() { ValueType = FieldValueType.ValueAsInt, Value = 1 } } } }
.Where(wi => wi.Fields.Any(f => 
       (f.ValueType == FieldValueType.ValueAsInt && f.Value >= 2)
    || (f.ValueType == FieldValueType.ValueAsString == "abc"))
    .ToList();

或者

db.Workitem.Add(new Workitem() { Fields = new List<Fields>() { new Field() { ValueType = FieldValueType.ValueAsDatetime, Value = DateTimeOffset.Now } } });

这应该与 IQueryable 和 IEnumerable 一起正常工作

我找到了几种解决方案,但在其中任何一个中都找不到一个方便的解决方案。以前,这是使用EAV实现的,现在一切都倾向于 postgresql 的JSONB列,因为 npgsql 可以使用它们。

我添加了这个配置,但是在反序列化时我已将“值”字段的类型指定为“对象”,我在反序列化时遇到了困难。

public override void Configure(EntityTypeBuilder<Workitem> builder)
{
    builder.HasKey(x => x.Id);
    builder.Property(x => x.Caption).HasMaxLength(150);
    builder.Property(x => x.Fields)
        .HasColumnType("jsonb")
        .HasConversion(
            v => JsonConvert.SerializeObject(v),
            v => JsonConvert.DeserializeObject<List<Field>>(v),
            new ValueComparer<List<Field>>(
                (c1, c2) => c1.SequenceEqual(c2),
                c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
                c => c.ToList()));
}

我还看到了每种数据类型都有一个单独的字段的实现,但这是一个糟糕的选择。

public struct Field
{
    public Guid Id { get; set; }
    public string Caption { get; set; }
    public FieldValueType ValueType { get; set; }

    public string _strValue;
    public int? _intValue;
    public double? _doubleValue;
    ...

    public dynamic Value //return _<type>Value
}

我想得到一个尽可能方便和简单的解决方案。

标签: c#entity-framework-corenpgsqlef-core-5.0

解决方案


推荐阅读