복잡한 데이터 유형 모음으로 UpdateModel을 호출하면 모든 비 바운드 값을 재설정 하시겠습니까?
-
05-07-2019 - |
문제
이것이 DefaultModelBinder 클래스의 버그인지 확실하지 않습니다. 그러나 UpdateModel은 일반적으로 일치하는 모델을 제외하고는 모델의 값을 변경하지 않습니다. 다음을 살펴보십시오.
[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Edit(List<int> Ids)
{
// Load list of persons from the database
List<Person> people = GetFromDatabase(Ids);
// shouldn't this update only the Name & Age properties of each Person object
// in the collection and leave the rest of the properties (e.g. Id, Address)
// with their original value (whatever they were when retrieved from the db)
UpdateModel(people, "myPersonPrefix", new string[] { "Name", "Age" });
// ...
}
일어나는 일은 Updatemodel을 생성하는 것입니다 새로운 개인 대상, ValueProvider에서 이름 및 연령 속성을 할당하고 인수 목록 <>에 넣습니다.이 속성의 나머지 부분을 기본 초기 값 (예 : ID = 0)으로 설정하게하십시오. 그래서 여기서 무슨 일이 일어나고 있습니까?
해결책
업데이트:MVC 소스 코드 (특히 DefaultModelBinder
수업) 그리고 여기에 내가 찾은 내용은 다음과 같습니다.
수업은 컬렉션을 바인딩하려고한다고 결정하여 메소드를 호출합니다. UpdateCollection(...)
내부를 만듭니다 ModelBindingContext
그게 a null
Model
재산. 그 후, 그 컨텍스트는 메소드로 전송됩니다 BindComplexModel(...)
확인합니다 Model
재산 null
그리고 a 새로운 이 경우 모델 유형의 인스턴스입니다.
그것이 값을 재설정하는 원인입니다.
따라서 양식/쿼리 문자열/경로 데이터를 통해 오는 값 만 채워지고 나머지는 초기 상태로 유지됩니다.
나는 거의 변경할 수 없었습니다 UpdateCollection(...)
이 문제를 해결합니다.
다음은 내 변경 사항이있는 방법입니다.
internal object UpdateCollection(ControllerContext controllerContext, ModelBindingContext bindingContext, Type elementType) {
IModelBinder elementBinder = Binders.GetBinder(elementType);
// build up a list of items from the request
List<object> modelList = new List<object>();
for (int currentIndex = 0; ; currentIndex++) {
string subIndexKey = CreateSubIndexName(bindingContext.ModelName, currentIndex);
if (!DictionaryHelpers.DoesAnyKeyHavePrefix(bindingContext.ValueProvider, subIndexKey)) {
// we ran out of elements to pull
break;
}
// **********************************************************
// The DefaultModelBinder shouldn't always create a new
// instance of elementType in the collection we are updating here.
// If an instance already exists, then we should update it, not create a new one.
// **********************************************************
IList containerModel = bindingContext.Model as IList;
object elementModel = null;
if (containerModel != null && currentIndex < containerModel.Count)
{
elementModel = containerModel[currentIndex];
}
//*****************************************************
ModelBindingContext innerContext = new ModelBindingContext() {
Model = elementModel, // assign the Model property
ModelName = subIndexKey,
ModelState = bindingContext.ModelState,
ModelType = elementType,
PropertyFilter = bindingContext.PropertyFilter,
ValueProvider = bindingContext.ValueProvider
};
object thisElement = elementBinder.BindModel(controllerContext, innerContext);
// we need to merge model errors up
VerifyValueUsability(controllerContext, bindingContext.ModelState, subIndexKey, elementType, thisElement);
modelList.Add(thisElement);
}
// if there weren't any elements at all in the request, just return
if (modelList.Count == 0) {
return null;
}
// replace the original collection
object collection = bindingContext.Model;
CollectionHelpers.ReplaceCollection(elementType, collection, modelList);
return collection;
}
다른 팁
Rudi Breedenraed는 방금 우수한 글을 썼습니다 게시하다 이 문제와 매우 유용한 솔루션을 설명합니다. 그는 DefaultModelBinder를 무시한 다음 컬렉션을 통해 업데이트 할 때 실제로 기본 MVC 동작과 같이 새로운 항목을 작성하는 대신 항목을 업데이트합니다. 이를 통해 updateModel () 및 tryupdatemodel () 동작은 루트 모델과 컬렉션과 일치합니다.
방금 ASP.NET MVC 2 소스 코드를 파헤칠 아이디어를주었습니다. 나는 지금 2 주 동안 이것으로 어려움을 겪고있다. 귀하의 솔루션이 중첩 된 목록에서 작동하지 않는다는 것을 알았습니다. 나는 updateCollection 방법에 중단 점을 넣고 결코 치지 않습니다. 이 방법을 호출하려면 모델의 루트 레벨이 목록이어야하는 것 같습니다.
이것은 내가 가지고있는 모델입니다. 나는 또한 일반 목록의 레벨이 하나 더 있지만 이것은 빠른 샘플입니다.
public class Borrowers
{
public string FirstName{get;set;}
public string LastName{get;set;}
public List<Address> Addresses{get;set;}
}
나는 무슨 일이 일어나고 있는지 알아 내기 위해 더 깊이 파고 들어야 할 것 같아요.
업데이트 : ASP.NET MVC 2에서 UpdateCollection이 여전히 호출되지만 위의 수정 문제는 이와 관련이 있습니다. 여기