分离实体不更新属性
本文关键字:属性 更新 实体 分离 | 更新日期: 2023-09-27 18:11:31
我有一个带有子实体的简单对象。当我更新分离对象时,子对象的属性没有保存。我读了很多关于这个表单的帖子,但不明白为什么它不更新。
查看这里更新实体的内部方法:
public class HtmlContent : ITextContentItem, ICreateStamp, IEditStamp, IImmutable
{
// ReSharper disable once UnusedMember.Local
private HtmlContent()
{
}
public HtmlContent(string name, string description, string text, DateTime creationDate,
DateTime? lastEditDate, ApplicationUser createdBy, ApplicationUser lastEditedBy)
{
this.Name = name;
this.Description = description;
this.Text = text;
this.CreationDate = creationDate;
this.LastEditDate = lastEditDate;
this.CreatedBy = createdBy;
this.LastEditedBy = lastEditedBy;
}
public HtmlContent(int id, string name, string description, string text, DateTime creationDate,
DateTime? lastEditDate, ApplicationUser createdBy, ApplicationUser lastEditedBy)
: this(name, description, text, creationDate, lastEditDate, createdBy, lastEditedBy)
{
this.Id = id;
}
public int Id { get; private set; }
public string Name { get; private set; }
public string Description { get; private set; }
public string Text { get; private set; }
public DateTime CreationDate { get; private set; }
public DateTime? LastEditDate { get; private set; }
public ApplicationUser CreatedBy { get; private set; }
public ApplicationUser LastEditedBy { get; private set; }
internal HtmlContent SetLastEditInfo(DateTime? lastEditDate, ApplicationUser lastEditedBy)
{
if ((lastEditDate.HasValue && lastEditedBy == null) ||
(!lastEditDate.HasValue && lastEditedBy != null))
{
throw new InvalidOperationException($"{nameof(lastEditDate)} and {nameof(lastEditedBy)} must be used together");
}
return new HtmlContent(this.Id, this.Name, this.Description, this.Text, this.CreationDate, lastEditDate, this.CreatedBy, lastEditedBy);
}
internal HtmlContent UpdateHtmlContent(string name, string description, string text)
{
return new HtmlContent(this.Id, name, description, text, this.CreationDate, this.LastEditDate, this.CreatedBy, this.LastEditedBy);
}
}
查看这里的更新方法:
public async Task Edit(int id, string name, string description, string text)
{
try
{
var content = await this.WithId(id);
this.db.Entry(content).State = EntityState.Detached;
var currentDate = DateTime.UtcNow;
var lastEditedBy = this.userProvider.GetCurrentUser();
content = content.SetLastEditInfo(currentDate, lastEditedBy);
content = content.UpdateHtmlContent(name, description, text);
this.db.Entry(content).State = EntityState.Modified;
await this.db.SaveChangesAsync();
}
catch (System.Data.Entity.Validation.DbEntityValidationException ex)
{
var errors = ex.EntityValidationErrors;
throw;
}
}
所有其他属性都更新得很好。只有LastEditedBy
没有更新。在Create方法中,CreatedBy
工作正确,因为它是保存到数据库的新实体。ApplicationUser
属性在代码生成的数据库中有外键
我可能错了,因为我从来没有尝试过这样做,但我可以看到你的模型有多个问题。
1。你的数据模型有私有的setter,没有无参数的构造函数。
你的数据模型应该只是一堆属性,它们有公共的setter和getter,以及一个无参数的构造函数。这允许EF代理导航属性,以便它"知道"何时设置了属性。
2。填充模型的代码在模型本身内部。
虽然这不是一个大问题,但它不允许您在将来使用通用存储库之类的东西。所有模型都必须知道如何操作自己,这可能导致一些不可读的代码。查看存储库模式
3。为导航属性定义外键
同样,虽然这不是100%重要,但它允许您设置相关实体,而不必首先从数据库中选择它们。您可以直接设置相关实体的Id。
4。你不应该创建一个新的实体来设置它的属性
它破坏了EF的跟踪,也破坏了实体的所有引用完整性。您希望实体在其生命周期内是相同的对象。这允许在不丢失对象和EF的任何跟踪的情况下修改属性。
我的建议是:
public class HtmlContent
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Text { get; set; }
public DateTime CreationDate { get; set; }
public DateTime? LastEditDate { get; set; }
public int CreatedById { get; set; }
public int LastEditedById { get; set; }
public ApplicationUser CreatedBy { get; set; }
public ApplicationUser LastEditedBy { get; set; }
}
public HtmlContentService
{
public async Task Edit(int id, string name, string description, string text)
{
try
{
var content = await this.WithId(id);
// no need to detach the object if you arent disposing the context
//this.db.Entry(content).State = EntityState.Detached;
var currentDate = DateTime.UtcNow;
var lastEditedBy = this.userProvider.GetCurrentUser();
// these methods could just be moved into this method
this.SetLastEditInfo(content, currentDate, lastEditedBy);
this.UpdateHtmlContent(content, name, description, text);
this.db.Entry(content).State = EntityState.Modified;
await this.db.SaveChangesAsync();
}
catch (System.Data.Entity.Validation.DbEntityValidationException ex)
{
var errors = ex.EntityValidationErrors;
throw;
}
}
private void SetLastEditInfo(
HtmlContent content,
DateTime lastEditDate,
ApplicationUser lastEditedBy)
{
if ((lastEditDate.HasValue && lastEditedBy == null) ||
(!lastEditDate.HasValue && lastEditedBy != null))
{
throw new InvalidOperationException(
$"{nameof(lastEditDate)} and {nameof(lastEditedBy)} must be used together");
}
content.LastEditDate = lastEditDate;
content.LastEditedBy = lastEditedBy;
}
private void UpdateHtmlContent(
HtmlContent content,
string name,
string description,
string text)
{
content.Name = name;
content.Description = description;
content.Text = text;
}
}
解决方案是在模型中创建一个外键属性(数据库中已经有一个外键,但是先用代码生成)。通过这种方式,如果存在LastEditedBy
用户,则可以在构造函数中设置该属性。
public class HtmlContent : ITextContentItem, ICreateStamp, IEditStamp, IImmutable
{
// ReSharper disable once UnusedMember.Local
private HtmlContent()
{
}
public HtmlContent(string name, string description, string text, DateTime creationDate,
DateTime? lastEditDate, ApplicationUser createdBy, ApplicationUser lastEditedBy)
{
this.Name = name;
this.Description = description;
this.Text = text;
this.CreationDate = creationDate;
this.LastEditDate = lastEditDate;
this.CreatedBy = createdBy;
this.LastEditedBy = lastEditedBy;
this.LastEditedById = LastEditedBy?.Id; // Set the id if it isn't null
}
public HtmlContent(int id, string name, string description, string text, DateTime creationDate,
DateTime? lastEditDate, ApplicationUser createdBy, ApplicationUser lastEditedBy)
: this(name, description, text, creationDate, lastEditDate, createdBy, lastEditedBy)
{
this.Id = id;
}
public int Id { get; private set; }
public string Name { get; private set; }
public string Description { get; private set; }
public string Text { get; private set; }
public DateTime CreationDate { get; private set; }
public DateTime? LastEditDate { get; private set; }
public ApplicationUser CreatedBy { get; private set; }
[ForeignKey("LastEditedById")] // Set the foreign key to existing property
public ApplicationUser LastEditedBy { get; private set; }
// Use a property in the model for saving and not just a property generated by code first in the database
public string LastEditedById { get; private set; }
internal HtmlContent SetLastEditInfo(DateTime? lastEditDate, ApplicationUser lastEditedBy)
{
if ((lastEditDate.HasValue && lastEditedBy == null) ||
(!lastEditDate.HasValue && lastEditedBy != null))
{
throw new InvalidOperationException($"{nameof(lastEditDate)} and {nameof(lastEditedBy)} must be used together");
}
return new HtmlContent(this.Id, this.Name, this.Description, this.Text, this.CreationDate, lastEditDate, this.CreatedBy, lastEditedBy);
}
internal HtmlContent UpdateHtmlContent(string name, string description, string text)
{
return new HtmlContent(this.Id, name, description, text, this.CreationDate, this.LastEditDate, this.CreatedBy, this.LastEditedBy);
}
}