Frage

Ist es möglich, eine Fremdschlüsselbeziehung auf meinem Modell zu einem Formulareingabe zu binden?

Sagen, ich habe eine Eins-zu-viele-Beziehung zwischen Car und Manufacturer. Ich möchte für die Aktualisierung Car eine Form haben, die für die Einstellung Manufacturer einen Auswahleingang umfasst. Ich hatte gehofft, das in der Lage sein zu tun, die sich im internen Modell zu binden, aber ich beginne ich zu denken, werde es selbst zu tun haben.

Meine Aktionsmethode Signatur sieht wie folgt aus:

public JsonResult Save(int id, [Bind(Include="Name, Description, Manufacturer")]Car car)

Die Form bucht die Werte Name, Beschreibung und Hersteller, in dem Hersteller ein Primärschlüssel des Typs int ist. Name und Beschreibung erhalten richtig eingestellt, aber nicht Hersteller, den Sinn, da das Modell Bindemittel macht hat keine Ahnung, was das PK-Feld ist. Bedeutet das, dass ich müsste dies eine benutzerdefinierte IModelBinder, dass es bewusst schreiben? Ich bin mir nicht sicher, wie das funktionieren würde, da meine Daten-Access-Repositorien durch einen IoC-Container auf jedem Controller Konstruktor geladen werden.

War es hilfreich?

Lösung

Hier ist mein nehmen - das Bindemittel ein benutzerdefiniertes Modell ist, dass, wenn sie GetPropertyValue gefragt, um zu sehen, sieht, wenn die Eigenschaft ist ein Objekt aus meinem Modell Montage und hat eine IRepository <> registriert in meinem ninject iKernel. Wenn es den IRepository von Ninject bekommen kann, verwendet es, dass die Fremdschlüssel-Objekt abzurufen.

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

}

Sie könnten natürlich Ninject für Ihre DI-Container und Ihren eigenen Repository-Typen ersetzen.

Andere Tipps

Sicher jedes Auto hat nur einen Hersteller. Wenn das der Fall ist, dann sollten Sie ein ManufacturerID Feld haben, dass Sie den Wert der Auswahl binden können. Das heißt, Ihre wählen sollte die Herstellernamen haben, wie es Text und die ID als Wert ist. In Ihrem speichern Wert binden ManufacturerID anstatt Hersteller.

<%= Html.DropDownList( "ManufacturerID",
        (IEnumerable<SelectListItem>)ViewData["Manufacturers"] ) %>

Mit

ViewData["Manufacturers"] = db.Manufacturers
                              .Select( m => new SelectListItem
                                            {
                                               Text = m.Name,
                                               Value = m.ManufacturerID
                                            } )
                               .ToList();

Und

public JsonResult Save(int id,
                       [Bind(Include="Name, Description, ManufacturerID")]Car car)

Vielleicht ist es ein spät, aber Sie können ein benutzerdefiniertes Modell Binder dies zu erreichen verwenden. Normalerweise würde ich es genauso wie @tvanofosson tun, aber ich hatte einen Fall, wo ich Userdetails zu den AspNetMembershipProvider Tabellen wurde hinzugefügt. Da ich auch nur POCO verwenden (und wo es dich von EntityFramework) Ich wollte nicht eine ID verwenden, da es nicht aus der betriebswirtschaftlichen Sicht gerechtfertigt war, so habe ich ein Modell nur hinzufügen / Benutzer registrieren. Dieses Modell hatte alle Eigenschaften für den Benutzer und eine Role-Eigenschaft als auch. Ich wollte einen Text Namen der Rolle binden, um es Rolemodel Darstellung ist. Das ist im Grunde, was ich getan habe:

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

Dann hatte ich folgende zum Global.asax hinzuzufügen:

ModelBinders.Binders.Add(typeof(RoleModel), new RoleModelBinder());

Und die Nutzung im Blick:

<%= Html.DropDownListFor(model => model.Role, new SelectList(Model.Roles, "RoleName", "RoleName", Model.Role))%>

Ich hoffe, das hilft dir.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top