Bik*_*net 1 c# entity-framework-core
在进行 EF 数据迁移时,我们有一堆带有预先设置的 guid 的 json,如下所示:
{
"Id": "61dcc24e9b524f10b69a5c3f17be8603",
"MakeName": "AUDI",
"ExternalId": "61dcc24e9b524f10b69a5c3f17be8604",
"CreatedBy": "System",
"CreatedOn": "2022/01/05"
},
{
"Id": "27a617d75b2e45bab513e2f336fcd921",
"MakeName": "BMW",
"ExternalId": "27a617d75b2e45bab513e2f336fcd927",
"CreatedBy": "System",
"CreatedOn": "2022/01/05"
},
Run Code Online (Sandbox Code Playgroud)
制作类
public class Make : AuditableEntity
{
public Make() { }
Guid Id { get; }
public String MakeName { get; set; }
public String CreatedBy { get; set; } = null!;
public DateTimeOffset CreatedOn { get; set; }
Guid ExternalId{ get; set; }
}
Run Code Online (Sandbox Code Playgroud)
然后我们使用通用种子函数来提取数据:
public static List<TEntity> SeedFromJson<TEntity>(string fileName)
{
string path = "../path/Seeds";
string fullPath = Path.Combine(path, fileName);
var result = new List<TEntity>();
using (StreamReader reader = new StreamReader(fullPath))
{
string json = reader.ReadToEnd();
result = JsonConvert.DeserializeObject<List<TEntity>>(json);
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
结果在自定义配置中使用,如下所示:
builder.HasData(LargeDataHelper.SeedFromJson<Make>("Makes.json"));
Run Code Online (Sandbox Code Playgroud)
问题是当我们运行迁移时。显示以下错误:
The seed entity for entity type 'Make' cannot be added because a default value was provided for the required property 'Id'. Please provide a value different from '00000000-0000-0000-0000-000000000000'.
Run Code Online (Sandbox Code Playgroud)
似乎在调试时result = JsonConvert.DeserializeObject用零替换了提供的 guid。
零是 a 的默认状态,Guid不可为空,因此new Guid()or的值Guid.Empty
这意味着该属性的反序列化已失败或未尝试
造成这种情况的主要原因有3个:
和Id属性ExternalId是私有的JsonConvert,因此默认情况下无法从进程访问
该Id属性是只读的,因此反序列化过程无法写入它。您应该通过添加 setter 使其可写。
Guid值的格式不正确,在 .Net 中, a 的序列化结构Guid应如下所示
{
"Id": "9694e5ec-9818-4987-abd8-6848132d3e4c",
}
Run Code Online (Sandbox Code Playgroud)
可以使用简单的JsonConverter来解决值解析,如以下小提琴所示: https: //dotnetfiddle.net/KKYTUT
public class GuidConverter : JsonConverter<Guid>
{
public GuidConverter()
{
}
public override void WriteJson(JsonWriter writer, Guid value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString("N"));
}
public override Guid ReadJson(JsonReader reader, Type objectType, Guid existingValue, bool hasExistingValue, JsonSerializer serializer)
{
string value = reader.Value.ToString();
return Guid.Parse(value);
}
public override bool CanRead
{
get { return true; }
}
}
Run Code Online (Sandbox Code Playgroud)
然后通过使属性公开且可写来更改模型以支持此转换器:
public class Make
{
public Make() {}
[JsonConverter(typeof(GuidConverter))]
public Guid Id { get; set; }
public String MakeName { get; set; }
public String CreatedBy { get; set; }
public DateTimeOffset CreatedOn { get; set; }
[JsonConverter(typeof(GuidConverter))]
public Guid ExternalId{ get; set; }
}
Run Code Online (Sandbox Code Playgroud)
这将适用于您当前的SeedFromJson方法,但也会影响此类型的序列化。
如果确实需要维护private值的访问修饰符和只读 状态Id,那么我们需要为此类型编写一个自定义类型转换器,或者可能需要添加其他结构更改来支持这一点。
执行此操作的一个简单方法可能是将OnDeserilized回调与ExtensionData 属性结合使用,这将捕获未能反序列化的 Json 令牌,我们可以在流程结束时引用它们。
这个小提琴显示了实现: https: //dotnetfiddle.net/sA7ZQl
此实现的额外好处是,它不需要此类之外的任何修改或依赖项,并且仅影响Deserilization和默认的 .Net序列化,因此来自控制器的响应将不受影响。
请注意,该Id属性已使用支持字段重新实现,这样我们就可以在构造函数之外设置该值。
public class Make
{
public Make() { }
private Guid Id { get { return _Id; } }
private Guid _Id;
public String MakeName { get; set; }
public String CreatedBy { get; set; }
public DateTimeOffset CreatedOn { get; set; }
private Guid ExternalId { get; set; }
[JsonExtensionData]
private IDictionary<string, JToken> _additionalData;
[System.Runtime.Serialization.OnDeserialized]
private void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
{
// Id and ExternalId are not public, so we capture the values into the Extension data to simplify psot processing
if (_additionalData.TryGetValue("Id", out JToken @id))
this._Id = Guid.Parse(id.ToString());
if (_additionalData.TryGetValue("ExternalId", out JToken @ExternalId))
this.ExternalId = Guid.Parse(@ExternalId.ToString());
}
}
Run Code Online (Sandbox Code Playgroud)
另请注意,在OnDeserilized方法中,我们在使用属性之前检查属性是否存在,调用者不会提供它们,因此首先检查是合理的。