ASP.NET MVC 모델을 바인딩하는 외국 키 관계
-
20-08-2019 - |
문제
내 모델의 외국 키 관계를 양식 입력에 바인딩 할 수 있습니까?
나는 사이에 일대일 관계가 있다고 말합니다 Car
그리고 Manufacturer
. 업데이트를위한 양식을 원합니다 Car
여기에는 설정을위한 선택 입력이 포함됩니다 Manufacturer
. 내장 모델 바인딩을 사용 하여이 작업을 수행 할 수 있기를 바랐지만 직접해야한다고 생각하기 시작했습니다.
내 액션 메소드 서명은 다음과 같습니다.
public JsonResult Save(int id, [Bind(Include="Name, Description, Manufacturer")]Car car)
양식은 값 이름, 설명 및 제조업체를 게시합니다. 여기서 제조업체는 유형의 주요 키입니다. int
. 이름과 설명은 제조업체가 아닌 제조업체가 아니지만 모델 바인더가 PK 필드가 무엇인지 전혀 모르기 때문에 의미가 있습니다. 그건 내가 관습을 써야한다는 뜻인가? IModelBinder
이것을 알고 있습니까? 내 데이터 액세스 리포지토리가 각각의 IOC 컨테이너를 통해로드되므로 어떻게 작동하는지 잘 모르겠습니다. Controller
건설자.
해결책
여기에 내 테이크가 있습니다 - 이것은 GetPropertyValue를 요청할 때, 속성이 내 모델 어셈블리의 개체인지, 린젝트 ikernel에 등록 된 irepository <>가 있는지 확인하는 사용자 정의 모델 바인더입니다. 린젝트에서 Irepository를 얻을 수 있다면이를 사용하여 외래 키 객체를 검색합니다.
public class ForeignKeyModelBinder : System.Web.Mvc.DefaultModelBinder
{
private IKernel serviceLocator;
public ForeignKeyModelBinder( IKernel serviceLocator )
{
Check.Require( serviceLocator, "IKernel is required" );
this.serviceLocator = serviceLocator;
}
/// <summary>
/// if the property type being asked for has a IRepository registered in the service locator,
/// use that to retrieve the instance. if not, use the default behavior.
/// </summary>
protected override object GetPropertyValue( ControllerContext controllerContext, ModelBindingContext bindingContext,
PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder )
{
var submittedValue = bindingContext.ValueProvider.GetValue( bindingContext.ModelName );
if ( submittedValue == null )
{
string fullPropertyKey = CreateSubPropertyName( bindingContext.ModelName, "Id" );
submittedValue = bindingContext.ValueProvider.GetValue( fullPropertyKey );
}
if ( submittedValue != null )
{
var value = TryGetFromRepository( submittedValue.AttemptedValue, propertyDescriptor.PropertyType );
if ( value != null )
return value;
}
return base.GetPropertyValue( controllerContext, bindingContext, propertyDescriptor, propertyBinder );
}
protected override object CreateModel( ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType )
{
string fullPropertyKey = CreateSubPropertyName( bindingContext.ModelName, "Id" );
var submittedValue = bindingContext.ValueProvider.GetValue( fullPropertyKey );
if ( submittedValue != null )
{
var value = TryGetFromRepository( submittedValue.AttemptedValue, modelType );
if ( value != null )
return value;
}
return base.CreateModel( controllerContext, bindingContext, modelType );
}
private object TryGetFromRepository( string key, Type propertyType )
{
if ( CheckRepository( propertyType ) && !string.IsNullOrEmpty( key ) )
{
Type genericRepositoryType = typeof( IRepository<> );
Type specificRepositoryType = genericRepositoryType.MakeGenericType( propertyType );
var repository = serviceLocator.TryGet( specificRepositoryType );
int id = 0;
#if DEBUG
Check.Require( repository, "{0} is not available for use in binding".FormatWith( specificRepositoryType.FullName ) );
#endif
if ( repository != null && Int32.TryParse( key, out id ) )
{
return repository.InvokeMethod( "GetById", id );
}
}
return null;
}
/// <summary>
/// perform simple check to see if we should even bother looking for a repository
/// </summary>
private bool CheckRepository( Type propertyType )
{
return propertyType.HasInterface<IModelObject>();
}
}
당신은 분명히 DI 컨테이너와 자신의 리포지토리 유형으로 Ninject를 대체 할 수 있습니다.
다른 팁
분명히 각 자동차에는 하나의 제조업체 만 있습니다. 이 경우 SELECT의 값을 바인딩 할 수있는 제조업체 분야가 있어야합니다. 즉, SELECT는 제조업체 이름을 텍스트로하고 ID를 값으로 가져야합니다. 저장 값에서 제조업체보다는 제조업체를 바인딩하십시오.
<%= Html.DropDownList( "ManufacturerID",
(IEnumerable<SelectListItem>)ViewData["Manufacturers"] ) %>
와 함께
ViewData["Manufacturers"] = db.Manufacturers
.Select( m => new SelectListItem
{
Text = m.Name,
Value = m.ManufacturerID
} )
.ToList();
그리고
public JsonResult Save(int id,
[Bind(Include="Name, Description, ManufacturerID")]Car car)
어쩌면 늦었을 수도 있지만 사용자 정의 모델 바인더를 사용하여이를 달성 할 수 있습니다. 일반적으로 @tvanofosson과 같은 방식으로 할 것입니다. 그러나 ASPNetMembershipprovider 테이블에 userDetails를 추가하는 경우가있었습니다. 또한 POCO 만 사용하고 (EntityFramework에서 매핑) ID를 사용하고 싶지 않기 때문에 비즈니스 관점에서 정당화되지 않았기 때문에 사용자를 추가/등록 할 수있는 모델 만 만들었습니다. 이 모델에는 사용자에 대한 모든 속성과 역할 속성도있었습니다. 역할의 텍스트 이름을 Rolemodel 표현에 묶고 싶었습니다. 그것이 기본적으로 내가 한 일입니다.
public class RoleModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
string roleName = controllerContext.HttpContext.Request["Role"];
var model = new RoleModel
{
RoleName = roleName
};
return model;
}
}
그런 다음 다음을 Global.asax에 추가해야했습니다.
ModelBinders.Binders.Add(typeof(RoleModel), new RoleModelBinder());
그리고 견해의 사용법 :
<%= Html.DropDownListFor(model => model.Role, new SelectList(Model.Roles, "RoleName", "RoleName", Model.Role))%>
이것이 도움이되기를 바랍니다.