سؤال

هل من الممكن ربط علاقة مفتاح خارجي على طراز بي إلى شكل مدخلات؟

ويقول لدي علاقة واحد لكثير بين Car وManufacturer. أريد أن يكون شكلا لتحديث Car يتضمن تحديد المدخلات لوضع Manufacturer. وكنت آمل أن تكون قادرة على القيام بذلك باستخدام نموذج المدمج في ملزم، ولكن أنا بدأت أعتقد سيكون لديك لتفعل ذلك بنفسي.

ولي طريقة عمل توقيع يبدو مثل هذا:

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

والوظائف شكل قيم الاسم والوصف والشركة المصنعة، حيث الصانع هو مفتاح أساسي من نوع int. الحصول على تعيين الاسم والوصف صحيح، ولكن ليس الشركة المصنعة، الأمر الذي يجعل الشعور منذ الموثق نموذج لديه أي فكرة عما هو الحقل PK. هل هذا يعني أنني يجب أن إرسال بريد IModelBinder عرف أنه على علم بذلك؟ لست متأكدا كيف يمكن أن تعمل منذ يتم تحميل بلدي مستودعات الوصول إلى البيانات من خلال وعاء اللجنة الاولمبية الدولية على كل منشئ Controller.

هل كانت مفيدة؟

المحلول

وهنا بلدي يأخذ - وهذا هو الموثق نموذج مخصص أنه عندما طلب منه GetPropertyValue، يتطلع إلى معرفة ما إذا كان العقار كائن من وجهة نظري تجميع النموذج، ولها IRepository <> مسجلة في بلدي NInject IKernel. إذا كان يمكن الحصول على IRepository من Ninject، ويستخدم ذلك لاسترداد الكائن المفتاح الخارجي.

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

}

هل يمكن أن تحل محل الواضح Ninject للحاوية DI ونوع خاص المخزون الخاص بك.

نصائح أخرى

وبالتأكيد كل سيارة لديها مصنع واحد فقط. إذا كان هذا هو الحال بعد ذلك يجب أن يكون حقل ManufacturerID أنه يمكنك ربط قيمة حدد ل. وهذا هو، لديك حدد يجب أن يكون اسم الصانع كما انها النص والهوية كقيمة. في القيمة حفظ الخاص بك، ربط ManufacturerID بدلا من الصانع.

<%= 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 ولكن كان لدي حالة حيث كنت مضيفا UserDetails إلى الجداول AspNetMembershipProvider. وبما أنني أيضا استخدام POCO فقط (وتعيين ذلك من EntityFramework) لم أكن أريد أن استخدام معرف لأنه لم يكن له ما يبرره من وجهة نظر رجال الأعمال لذلك أنا خلقت نموذجا فقط إلى إضافة / تسجيل المستخدمين. كان هذا نموذج عن خصائص المستخدم وخاصية دور كذلك. كنت أرغب في ربط النص اسم دور لانها 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