Szukaj…


Wprowadzenie

Powiązanie modelu to proces pobierania parametrów HTTP, zwykle w ciągu zapytania zapytania GET lub w treści POST, i stosowania go do obiektu, który można następnie zweryfikować i wykorzystać w sposób zorientowany obiektowo bez potrzeby wykonywania działań kontrolera mając dogłębną wiedzę na temat pobierania parametrów HTTP.

Innymi słowy, wiązanie modelu pozwala działaniom w MVC mieć albo parametr (parametry), bez względu na to, czy jest to typ wartości, czy obiekt.

Uwagi

Aby spróbować utworzyć instancję w akcji, proces modelu powiązania przeszuka dane w różnych miejscach:

  • Tworzyć dane
  • Dane trasy
  • Ciąg zapytania
  • Pliki niestandardowe (na przykład pliki cookie)

Wiązanie wartości trasy

Biorąc pod uwagę niektóre domyślne routing, takie jak {controller=Home}/{action=Index}/{id?} Jeśli masz adres URL https://stackoverflow.com/questions/1558902

Przechodziłoby to do pytaniaController, a wartość 1558902 byłaby odwzorowana na parametr id akcji indeksu, tj.

public ActionResult Index(int? id){
     //id would be bound to id of the route
}

Wiązanie ciągu zapytania

Aby przedłużyć wiązanie trasy, powiedz, że masz adres URL taki jak https://stackoverflow.com/questions/1558902?sort=desc

i routing jak {controller=Home}/{action=Index}/{id?}

public ActionResult Index(int? id, string sort){
     //sort would bind to the value in the query string, i.e. "desc"
}

Wiązanie z przedmiotami

Często pracujesz z klasami viewmodel w asp.net-mvc i chciałbyś powiązać z nimi właściwości. Działa to podobnie do mapowania na poszczególne parametry.

Załóżmy, że masz takie proste wywołanie modelu widoku PostViewModel w ten sposób

public class PostViewModel{
  public int Id {get;set;}
  public int SnappyTitle {get;set;}
}

Następnie opublikowałeś wartości Id i SnappyTitle z formularza w żądaniu http, a następnie zmapowałyby bezpośrednio na tym modelu, gdyby sam model był parametrem akcji, np.

public ActionResult UpdatePost(PostViewModel viewModel){
  //viewModel.Id would have our posted value
}

Warto zauważyć, że w powiązaniu nie jest rozróżniana wielkość liter w nazwach parametrów i właściwości. W miarę możliwości będzie także rzucać wartości. Zostawiam więcej przypadków krawędzi dla konkretnych przykładów

Wiązanie Ajax

Są to wartości formularzy, które trafiają do żądania HTTP przy użyciu metody POST. (w tym żądania POST jQuery).

Powiedzmy, że zrobiłeś post w ajax

$.ajax({
    type: 'POST',
    url: window.updatePost,
    data:  { id: 21, title: 'snappy title' },
    //kept short for clarity
});

Tutaj dwie wartości w json, id i title, byłyby powiązane z pasującą akcją, np

public JsonResult UpdatePost(int id, string title) {
    ...
}

Ogólne, wiązanie modelu na podstawie sesji

Czasami potrzebujemy zachować cały model i przenieść go między działaniami, a nawet kontrolerami. Przechowywanie modelu na sesji dobre rozwiązanie dla tego rodzaju wymagań. Jeśli połączymy to z potężnymi funkcjami wiązania modelu MVC, otrzymamy elegancki sposób na zrobienie tego. Możemy utworzyć ogólne powiązanie oparte na modelu sesji w trzech prostych krokach:

Krok pierwszy: Utwórz model spoiwa

Utwórz sam model spoiwa. Osobiście stworzyłem klasę SessionDataModelBinder w folderze / Infrastructure / ModelBinders .

using System;
using System.Web.Mvc;

public class SessionDataModelBinder<TModel>
    : IModelBinder
    where TModel : class
{
    private string SessionKey { get; set; }

    public SessionDataModelBinder(string sessionKey)
    {
        if (string.IsNullOrEmpty(sessionKey))
            throw new ArgumentNullException(nameof(sessionKey));
        SessionKey = sessionKey;
    }

    public object BindModel(
        ControllerContext controllerContext, 
        ModelBindingContext bindingContext)
    {
        // Get model from session
        TModel model = controllerContext
            .HttpContext
            .Session[SessionKey] as TModel;
        // Create model if it wasn't found from session and store it
        if (model == null)
        {
            model = Activator.CreateInstance<TModel>();
            controllerContext.HttpContext.Session[SessionKey] = model;
        }
        // Return the model
        return model;
    }
}

Krok drugi: zarejestruj spoiwo

Jeśli mamy model jak poniżej:

public class ReportInfo
{
    public int ReportId { get; set; }
    public ReportTypes TypeId { get; set; }
}

public enum ReportTypes
{
    NotSpecified,
    Monthly, Yearly
}

Możemy zarejestrować segregator oparty na sesjach dla tego modelu w Global.asax w metodzie Application_Start :

protected void Application_Start()
{
    .........

    // Model binders.
    // Remember to specy unique SessionKey
    ModelBinders.Binders.Add(typeof(ReportInfo), 
        new SessionDataModelBinder<ReportInfo>("ReportInfo"));
}

Krok trzeci: użyj go!

Teraz możemy skorzystać z tego modelu spoiwa, po prostu dodając parametr do naszych działań :

public class HomeController : Controller
{
    public ActionResult Index(ReportInfo reportInfo)
    {
        // Simply set properties
        reportInfo.TypeId = ReportTypes.Monthly;

        return View();
    }

    public ActionResult About(ReportInfo reportInfo)
    {
        // reportInfo.TypeId is Monthly now because we set
        // it previously in Index action.
        ReportTypes currentReportType = reportInfo.TypeId;

        return View();
    }
}

Zapobiegaj wiązaniu na PostModel

Biorąc pod uwagę model (post):

public class User
{
    public string FirstName { get; set; }
    public bool IsAdmin { get; set; }
}

Z takim widokiem:

@using (Html.BeginForm()) {
    @Html.EditorFor(model => model.FirstName)
    <input type="submit" value="Save" />       
}

Aby zapobiec przypisywaniu IsAdmin przez złośliwego użytkownika, możesz użyć atrybutu Bind w akcji:

[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
    // ...
}

Udostępnianie pliku

Model:

public class SampleViewModel
{
    public HttpPostedFileBase file {get;set;}
}

Widok:

@model HelloWorldMvcApp.SampleViewModel

@using (Html.BeginForm("Index","Home",FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    <div class="form-group">
        @Html.TextBoxFor(model => model.file, new {@class="form-control", type="file"}) 
        @Html.ValidationMessageFor(model => model.file)
    </div>

    <button type="submit" class="btn btn-success submit">Upload</button>
}

Akcja:

[HttpPost]
public ActionResult Index(SampleViewModel model)
{                
    
    if (model.file.ContentLength > 0) 
    {
        string fileName = Path.GetFileName(model.file.FileName);
        string fileLocation = "~/App_Data/uploads/"+ fileName;
        model.file.SaveAs(Server.MapPath(fileLocation));
    }
    return View(model);
}

Ręczne sprawdzanie poprawności pól dat za pomocą formatów dynamicznych za pomocą segregatora modeli

Jeśli różni użytkownicy potrzebują innego formatu daty i godziny, może być konieczne przeanalizowanie ciągu daty przychodzącej do rzeczywistej daty zgodnie z tym formatem. W takim przypadku ten fragment może ci pomóc.

public class DateTimeBinder : DefaultModelBinder 
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {

                var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
                DateTime date;
                var displayFormat = Session["DateTimeFormat"];
                if (value.AttemptedValue != "")
                {
                    if (DateTime.TryParseExact(value.AttemptedValue, displayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
                    {
                        return date;
                    }
                    else
                    {
                        bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Invalid date format");
                    }
                }
            }

        return base.BindModel(controllerContext, bindingContext);
    }


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow