문제

내 모델의 외국 키 관계를 양식 입력에 바인딩 할 수 있습니까?

나는 사이에 일대일 관계가 있다고 말합니다 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))%>

이것이 도움이되기를 바랍니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top