Asp.net mvc MVC中具有存储库/DTO模式的实体的部分更新(为API做准备)

Asp.net mvc MVC中具有存储库/DTO模式的实体的部分更新(为API做准备),asp.net-mvc,asp.net-mvc-3,web-applications,architecture,asp.net-web-api,Asp.net Mvc,Asp.net Mvc 3,Web Applications,Architecture,Asp.net Web Api,我已经构建了我的域模型层、存储库层,现在我正在处理一个webApi项目所使用的DTO层。我正在执行一个更新服务方法,我想知道部分更新。这是我的DTO课程: public class FullPersonDto { public FullPersonDto() { Friends = new List<Person>(); } public FullPersonDto(Person per

我已经构建了我的域模型层、存储库层,现在我正在处理一个webApi项目所使用的DTO层。我正在执行一个更新服务方法,我想知道部分更新。这是我的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;
        }
    }
公共类FullPersondo
{
公共FullPersonDto()
{
朋友=新列表();
}
公共FullPersonDto(个人)
{
PersonId=person.PersonId;
DateCreated=person.DateCreated;
细节=人。细节;
朋友=新列表();
foreach(亲自交朋友。朋友)
{
添加(新的PersonDto(friend));
}
}
[关键]
公共int PersonId{get;set;}
[必需]
公共虚拟日期时间DateCreated{get;set;}
公共虚拟字符串详细信息{get;set;}
公共列表朋友{get;set;}
公众人士实体()
{
var person=新的人
{
PersonId=PersonId,
DateCreated=(DateTime)DateCreated,
细节=细节,
朋友=新列表()
};
foreach(朋友中的人对朋友)
{
person.Friends.Add(friend.ToEntity());
}
返回人;
}
}
以下是我的存储库中的更新方法:

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;
        }
public Person UpdatePerson(个人)
{
风险值条目=_db.条目(人);
if(entry.State==EntityState.Detached)
{
var dbSet=_db.Set();
Person attachedPerson=dbSet.Find(Person.PersonId);
if(attachedPerson!=null)
{
var attachedEntry=_db.Entry(attachedPerson);
attachedEntry.CurrentValues.SetValues(person);//如果值为null,如ID或DateCreated,该怎么办?
}
其他的
{
entry.State=EntityState.Modified;
}
}
保存更改();
返回人;
}
我的问题是:如果我只需要通过我的webAPI更新一个人的详细信息怎么办?是构造一个完整的PersonDto并使用SetValues更新整个对象的约定,还是有任何方法可以指定我只需要更新一个字段,这样我就不必通过线路发送大量数据(我真的不需要)

如果可以进行部分更新,那么什么时候更新整个实体比较好?即使我必须更新5/7属性,它也要求我发送2/7的旧数据以进行重新写入,这样SetValues就不会将null从DTO写入我的字段


这里的任何帮助都会很棒。。。对这些东西完全陌生,并且努力把一切都学好。多谢各位

您有几个选项,您可以创建一个存储过程来更新所需的部分(我不会这样做),也可以在使用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();
}

这将取决于您的场景,您想做什么,但一般来说,发送整个实体进行更新是很好的,这就是我将如何处理您的情况,如果需要,您的情况可能会在未来发生变化

我采用了类似的方法来进行优化,并且在附加空值时也遇到了同样的问题(不仅仅是空值,布尔值也会有问题)。这就是我想到的:

    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 dto)
式中T:类,属性
{
TEntity=context.Set().Local.FirstOrDefault(x=>x.Id==dto.Id);
if(tenty==null)
{
tenty=context.Set().Create();
TEntity.Id=dto.Id;
context.Set().Attach(tenty);
}
context.Entry(tenty).CurrentValues.SetValues(dto);
var attribute=dto.GetAttribute();
if(属性!=null)
{
foreach(attribute.Properties中的var属性)
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; }
}
公共接口IDTO
{
int Id{get;set;}
}
公共界面的开放性
{
int Id{get;set;}
可为空的修改{get;set;}
可为空的已创建{get;set;}
}
我正在使用我的自定义EnsureUpdateAttribute注释应始终更新的属性(以处理未跟踪的空值/默认值):

公共类EnsureUpdateAttribute:属性
{
公共IEnumerable属性{get;private set;}
公共EnsureUpdateAttribute(参数字符串[]属性)
{
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();
    }
公共类示例:IEntity
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共bool活动{get;set;}
公共可空修改{get;set;}
已创建公共可为null的{get;set;}
}
[EnsureUpdated(“Active”)]///实体框架更改跟踪要求,了解存根实体
公共类样本收件人:IDTO
{
公共int Id{get;set;}
[必需]
公共字符串名称{get;set;}
[JsonIgnore]///如何将属性排除在连接之外/忽略以进行序列化
公共bool活动{get;set;}
}
[HttpPost]
公共httpresponsemessagesavesample(SampleDTO到dto)
{
dto.Active=true;
_ctx.AddModel(dto);
_ctx.SaveChanges();
返回NoContent();
}
return NoContent()只是返回204(NoContent)的扩展

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();
    }