Как прокинуть второе свойство в конвертер Entity Framework?
Есть entity Data и класс ее конфигурации. Данные Value хранятся в строковом виде. Как можно прокинуть Type, чтобы преобразовать Value в нужный тип? Можно ли это сделать если Type будет не в текущей, а в связанной сущности?
public enum DataType
{
Date,
Decimal,
String,
Json
}
public class Data
{
[Key]
public int Id { get; set; }
public DataType Type { get; set; }
public object Value { get; set; }
}
public class DataEntityConfiguration : IEntityTypeConfiguration<Data>
{
public void Configure(EntityTypeBuilder<Data> builder)
{
builder
.Property(e => e.Value)
.HasConversion(
v => v.ToString(),
v => /* Connvert using Type property */);
}
}
Ответы (2 шт):
Автор решения: return
→ Ссылка
Самый простой способ:
public abstract class Data
{
[Key]
public int Id { get; set; }
internal Data() { } // если вы не хотите, чтоб др. сборки могли наследовать этот
// класс (в .NET нет нормального способа ограничить наследование)
public abstract override string ToString();
public abstract object AsObject();
public virtual Type GetObjectType() => AsObject().GetType();
}
public sealed /*optional*/ class StringData : Data
{
public string Value { get; set; }
public StringData(string? value) => Value = value ?? "";
public override stirng ToString() => Value;
public override object AsObject() => Value;
public override Type GetObjectType() => typeof(string);
public static implicit operator string(StringData data)
{
if (data == null) throw new ArgumentNullException("data");
return data.ToString(); // для устойчивости к изменениям
}
public static implicit operator StringData(string str) { /*...*/ }
}
Использование:
public class DataEntityConfiguration : IEntityTypeConfiguration<Data>
{
public void Configure(EntityTypeBuilder<Data> builder)
{
builder
.Property(e => e.AsObject())
.HasConversion(
v => v.ToString(),
v => /*convert using GetObjectType()*/);
}
}
Вы можете заменить/совместить GetObjectType() на/с атрибут/-ом DataObjectTypeAttribute, например:
[AttributeUsage(AttributeTargets.Class, AllowMultiply = false, Inherit = false
/*if 'sealed class StringData'*/)]
public sealed class DataObjectTypeAttribute : Attribute
{
public Type ObjectType { get; }
public DataObjectTypeAttribute(Type objectType) { /*...*/ }
}
[DataObjectType(typeof(string))]
public sealed class StringData : Data
{
// ...
}
using System.Reflection;
// ...
// in Data type
// ...
public static Type GetObjectType(Type dataType)
{
if (dataType == null) throw new ArgumentNullException("dataType");
if (!dataType.IsSubclassOf(Data))
throw new ArgumentException("Data type must be subclass of 'Data' type", "dataType");
return dataType.GetCustomAttribute<DataObjectTypeAttribute>()?.ObjectType ??
typeof(object); // или выбросить исключение, если атрибут не найден
}
// ...
Использование:
public class DataEntityConfiguration : IEntityTypeConfiguration<Data>
{
public void Configure(EntityTypeBuilder<Data> builder)
{
builder
.Property(e => e.AsObject())
.HasConversion(
v => v.ToString(),
v => /*convert using Data.GetObjectType(e.GetType())*/);
}
}
Автор решения: return
→ Ссылка
Можно взять и создать вот такие классы:
public static class DataServices
{
public static void Check(this DataType dataType)
{
// ...
}
public static Type GetClrType(this DataType dataTypew)
{
// ...
}
public static DataType GetDataType(Type clrType)
{
// ...
}
}
public readonly struct DataAdapter // or 'DataConverter'
{
public object Adapt /*or 'Convert'*/(string data, DataType dataType)
{
dataType.Check();
return dataType switch
{
DataType.String => data;
DataType.Decimal => decimal.Parse(data);
DataType.DateTime => DateTime.Parse(data);
DataType.Json => // ...
// в случае ошибки исключение будет получено из метода DataServices.Check
_ => throw new InvalidProgramException();
}
}
public object Adapt(string data, out DataType dataType)
{
if (decimal.TryParse(data, out var dec))
{
dataType = DataType.Decimal;
return dec;
}
else if (DateTime.TryParse(data, out var dateTime))
{
dataType = DataType.DateTime;
return dateTime;
}
else if (/*try parse data as json*/)
{
dataType = DataType.Json;
return json;
}
else
{
dataType = DataType.String;
return data;
}
}
}
Лучше переделать класс Data:
public class Data
{
string data;
[Key]
public int Id { get; set; }
public object Data
{
get => data;
set
{
if (value == null) throw new ArgumentNullException("value");
data = value;
CachedData = null;
}
}
public DataType DataType { get; set; }
public string? CachedData { get; private set; } // optional
public void SetData(string data)
{
DataType dataType;
Data = new DataAdapter().Adapt(data, out dataType);
DataType = dataType;
CachedData = data;
}
}