我已经构建了我的域模型层,我的存储库层,现在我正在处理我的DTO层,以供webApi项目使用。我正在实现更新服务方法,我想知道部分更新。这是我的DTO课程:
public class FullPersonDto
{
public FullPersonDto()
{
Friends = new List<Person>();
}
public FullPersonDto(Person person)
{
PersonId = person.PersonId;
DateCreated = person.DateCreated;
Details = person.Details;
Friends = new List<Person>();
foreach (Person friend in person.Friends)
{
Friends.Add(new PersonDto(friend));
}
}
[Key]
public int PersonId { get; set; }
[Required]
public virtual DateTime DateCreated { get; set; }
public virtual string Details { get; set; }
public List<Person> Friends { get; set; }
public Person ToEntity()
{
var person = new Person
{
PersonId = PersonId,
DateCreated = (DateTime) DateCreated,
Details = Details,
Friends = new List<Person>()
};
foreach (PersonDto friend in Friends)
{
person.Friends.Add(friend.ToEntity());
}
return person;
}
}
这是我的存储库中的Update方法:
public Person UpdatePerson(Person person)
{
var entry = _db.Entry(person);
if (entry.State == EntityState.Detached)
{
var dbSet = _db.Set<Person>();
Person attachedPerson = dbSet.Find(person.PersonId);
if (attachedPerson != null)
{
var attachedEntry = _db.Entry(attachedPerson);
attachedEntry.CurrentValues.SetValues(person); // what if values are null, like ID, or DateCreated?
}
else
{
entry.State = EntityState.Modified;
}
}
SaveChanges();
return person;
}
我的问题是:如果我只需要通过我的webAPI更新某个人的详细信息怎么办?是使用SetValues构造整个PersonDto和更新整个对象的约定,还是有任何方法可以指定我只需要更新单个字段,这样我就不必通过线路发送大量数据(我真的不需要)?
如果 可以进行部分更新,那么何时更新整个实体是否合适?即使我必须更新5/7属性,它也要求我发送2/7的旧数据来重写,以便SetValues不会从我的DTO中将空值写入我的字段。
这里的任何帮助都会很棒......对这些东西来说是全新的,并试图正确地学习一切。谢谢。
答案 0 :(得分:1)
我采取了类似的方法来进行优化,并且我在连接时遇到了与null值相同的问题(不仅仅是null,你也会遇到布尔问题)。这就是我想出来的:
public static void Update<T>(this DbContext context, IDTO dto)
where T : class, IEntity
{
T TEntity = context.Set<T>().Local.FirstOrDefault(x => x.Id == dto.Id);
if (TEntity == null)
{
TEntity = context.Set<T>().Create();
TEntity.Id = dto.Id;
context.Set<T>().Attach(TEntity);
}
context.Entry(TEntity).CurrentValues.SetValues(dto);
var attribute = dto.GetAttribute<EnsureUpdatedAttribute>();
if (attribute != null)
{
foreach (var property in attribute.Properties)
context.Entry(TEntity).Property(property).IsModified = true;
}
}
这是DbContext的扩展方法。以下是IDTO和IEntity的接口:
public interface IDTO
{
int Id { get; set; }
}
public interface IEntity
{
int Id { get; set; }
Nullable<DateTime> Modified { get; set; }
Nullable<DateTime> Created { get; set; }
}
我正在使用我的自定义EnsureUpdatedAttribute来注释应该始终更新哪些属性(以处理未跟踪的空值/默认值):
public class EnsureUpdatedAttribute : Attribute
{
public IEnumerable<string> Properties { get; private set; }
public EnsureUpdatedAttribute(params string[] properties)
{
Properties = properties.AsEnumerable();
}
}
这是一个使用示例:
public class Sample : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public Nullable<DateTime> Modified { get; set; }
public Nullable<DateTime> Created { get; set; }
}
[EnsureUpdated("Active")] /// requirement for entity framework change tracking, read about stub entities
public class SampleDTO : IDTO
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[JsonIgnore] /// How to exclude property from going on the wire / ignored for serialization
public bool Active { get; set; }
}
[HttpPost]
public HttpResponseMessage SaveSample(SampleDTO dto)
{
dto.Active = true;
_ctx.AddModel<Sample>(dto);
_ctx.SaveChanges();
return NoContent();
}
return NoContent()只是返回204(NoContent)的扩展名。
希望这有帮助。
答案 1 :(得分:0)
您有几个选项,您可以创建存储过程来更新所需的部分(我不会这样做),或者您可以在使用EF保存上下文更改之前手动选择要在模型上更新的文件。
以下是如何更新特定字段的示例:
public void UpdatePerson(int personId, string details)
{
var person = new Person() { Id = personId, Details = details };
db.Persons.Attach(personId);
db.Entry(person).Property(x => x.Details).IsModified = true;
db.SaveChanges();
}
这将取决于您的方案您想要做什么,但一般来说可以发送您的整个实体进行更新,这就是我如何处理您的情况,如果需要可能在将来发生变化。