是否有方法用实体框架核心填充字典属性?
出于性能原因,我们喜欢在应用程序中搜索,而不是在数据库中搜索。由于一份清单不能很好地按比例排列,我们喜欢使用字典。
例如(简化的例子)
class Course
{
public Dictionary<string, Person> Persons { get; set; }
public int Id { get; set; }
}
class Person
{
public string Firstname { get; set; }
public string Lastname { get; set; }
}
我试过的东西
HasConversion
),但是转换只在单个项上工作,而在集合上不起作用。HasMany
已经提供了一个编译错误:
构建器.HasMany(c => c.Persons) //将不编译,Person不是IEnumerable .WithOne().HasForeignKey("PersonId");Collection
并实现InsertItem
、SetItem
等)--不幸的是,这也无法工作,因为etc将将项添加到集合中,然后首先填充属性(至少在演示中不使用我们的OwnsOne属性)--之后不会调用SetItem
。当然,我可以在存储库中构建一个字典(使用存储库模式),但这很棘手,因为我可能会忘记某些部分--与运行时错误和声明性风格相比,我更喜欢编译时错误而不是命令式风格代码。
更新,以明确
List
而不是字典,映射就会起作用发布于 2020-04-17 09:52:56
我不认为保存字典是个好主意(我甚至无法想象如何在数据库中保存字典)。正如我从您的源代码中看到的,您正在使用FirstName作为密钥。在我看来,你应该把字典改成HashSet。这样,您可以保持速度,但也保存到数据库。下面是一个示例:
class Course
{
public Course() {
this.People = new HashSet<Person>();
}
public ISet<Person> People { get; set; }
public int Id { get; set; }
}
在此之后,您可以从它创建一个字典,或者继续使用哈希集。字典样本:
private Dictionary<string, Person> peopleDictionary = null;
public Dictionary<string, Person> PeopleDictionary {
get {
if (this.peopleDictionary == null) {
this.peopleDictionary = this.People.ToDictionary(_ => _.FirstName, _ => _);
}
return this.peopleDictionary;
}
}
请注意,这将意味着您的人员集将成为非同步后,您添加/删除到/从字典。为了同步更改,您应该在上下文中覆盖SaveChanges方法,如下所示:
public override int SaveChanges() {
this.SyncPeople();
return base.SaveChanges();
}
public override int SaveChanges(bool acceptAllChangesOnSuccess) {
this.SyncPeople();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) {
this.SyncPeople();
return base.SaveChangesAsync(cancellationToken);
}
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) {
this.SyncPeople();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
private void SyncPeople() {
foreach(var entry in this.ChangeTracker.Entries().Where(_ = >_.State == EntityState.Added || _.State == EntityState.Modified)) {
if (entry.Entity is Course course) {
course.People = course.PeopleDictionary.Values.ToHashSet();
}
}
}
编辑:为了有一个正在运行的代码,您需要通过NotMapped属性告诉EF不要映射字典。
[NotMapped]
public Dictionary<string, Person> PeopleDictionary { ... }
发布于 2020-04-16 08:35:05
似乎有人一直在努力解决这个问题并找到了解决办法。请参阅:使用EFCore2.1将字典存储为JSON字符串
该实体的定义如下:
public class PublishSource
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Dictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
}
在数据库上下文的OnModelCreating方法中,我只调用HasConversion,它执行字典的序列化和反序列化:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PublishSource>()
.Property(b => b.Properties)
.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<Dictionary<string, string>>(v));
}
但是,我注意到的一件重要事情是,当更新实体和更改字典中的项时,EF更改跟踪不会发现字典已经更新的事实,因此您需要显式地调用DbSet<>上的Update方法来将实体设置为在更改跟踪器中修改。
发布于 2020-04-17 22:21:47
您可以添加一个新的属性PersonsJson
来存储JSON数据。当数据从DB检索或存储到DB时,它会自动序列化或反序列化JSON数据到Persons
属性中。Persons
属性未映射,只映射PersonsJson
。
class Course
{
[NotMapped]
public Dictionary<string, Person> Persons { get; set; }
public string PersonsJson
{
get => JsonConvert.SerializeObject(Persons);
set => Persons = JsonConvert.DeserializeObject<Dictionary<string, Person>>(value);
}
public int Id { get; set; }
}
https://stackoverflow.com/questions/60726966
复制