MVC model duplicatie

Ik ben erg nieuw voor ASP.NET MVC, dus vergeef me als dit iets is dat ik moet weten. Ik heb er geen duidelijke documentatie over gezien, dus hier geldt:

Ik heb een LINQ to Entities-gegevensmodel en een MVC-project. Ik gebruik veel javascript/jQuery, dus ik heb ervoor gekozen om toegang te krijgen tot mijn gegevens van de client via een WebAPI als json-objecten. Ik wil echter niet alle entiteitobjecteigenschappen doorgeven aan de client, dus heb ik afzonderlijke modellen toegevoegd aan mijn MVC-project waarin ik MVC-modelvalidatie en binding aan mijn weergaven verwerk. Ook heb ik json-versies van de modellen gemaakt om ermee in mijn jQuery te werken.

Dit is slechts het begin van het project en ik wil het niet op de verkeerde voet starten. Het hebben van drie versies van mijn modellen voor elke entiteit in mijn bedrijfslaag zal een nachtmerrie worden! Ik weet zeker dat de algemene structuur van mijn project een heel gebruikelijke is, maar ik zie niet dat veel ontwikkelaars genoegen nemen met een dergelijke duplicatie van code. Er moet een betere manier zijn om het te implementeren.

Wat denk jij? Waardeer elke input echt.

0
Waarom heb je een andere versie voor JSON? Bekijk modellen zijn nooit echt zoals EF-modellen, omdat je meestal weinig velden laat vallen en nieuwe toevoegt, dus er is geen codeduplicatie.
toegevoegd de auteur LukLed, de bron
Er moet een aanzienlijke overlap zijn, dus er is sprake van duplicatie. Hoe zou u zonder JSON entiteiten maken en naar de WebAPI verzenden?
toegevoegd de auteur hofnarwillie, de bron

2 antwoord

In antwoord op uw opmerking hierboven - u kunt uw JavaScript-viewmodel maken als een standaard js-object. Ik gebruik Knockout.js meestal zo dat het er zo uitziet:

jsController.Resource = function (data) {

    self.UserId = ko.observable(data.UserId);
    self.FullName = ko.observable(data.Name);
    self.RoleName = ko.observable(data.RoleName);
    self.RoleId = ko.observable(data.RoleId); 
} 

en gebruik vervolgens een ajax-postmethode om deze naar je MVC-actie te plaatsen

jsController.addToUndertaking = function (resource, isAsync) {
    mylog.log("UndertakingId at post = " + jsController.undertakingId);

    var action = $.ajax({
        type: "POST",
        url: "/TeamMember/AddUserToUndertaking",
        data: resource,
        cache: false,
        async: isAsync
    });

    action.done(function() {
        resource.AllocatedToUndertaking(true);
    //Do other funky stuff
    });
};  

Maak uw MVC-actie zodat een verzameling formulieren als volgt wordt geaccepteerd:

public ActionResult AddUserToUndertaking(FormCollection postedResource)
    {
        if (Request.IsAjaxRequest() == false)
        {
            const string msg = "Non ajax request received";
            Logger.ErrorFormat(msg);
            throw new SecurityException(msg);
        }

        if (postedResource == null)
        {
            Logger.Debug("Null resource posted - terminating.");
            return new HttpStatusCodeResult(500);
        }

        var resource = new AllocatedResourceAjaxViewModel(postedResource);
    //Do something Funky
       return new HttpStatusCodeResult(200);
    }

en vervolgens maak je je MVC-viewmodel uit de formulierenverzameling (ik doe dit meestal door de formulierenverzameling door te geven als een constructormethode voor het viewmodel).

public class AllocatedResourceAjaxViewModel
{
    public int UserId { get; set; }
    public string Name { get; set; }
    public string RoleName { get; set; }
    public int RoleId { get; set; }


    public AllocatedResourceAjaxViewModel()
    {}

    public AllocatedResourceAjaxViewModel(NameValueCollection formData)
    {
        UserId = JsFormDataConverter.Int(formData["UserId"]);
        Name = Convert.ToString(formData["FullName"]);
        RoleName = Convert.ToString(formData["RoleName"]);
        RoleId = JsFormDataConverter.Int(formData["RoleId"]);

    }

}

Omdat een nul int in je JavaScript VM leidt tot een reeks 'undefined' die wordt doorgegeven, heb je een converter-methode nodig om niet-strings veilig te extraheren.

public static class JsFormDataConverter
{
    public static bool Boolean(string formValue, bool defaultValue = false)
    {
        if (formValue.ToLower() == "true") return true;
        if (formValue.ToLower() == "false") return false;
        return defaultValue;
    }


    public static int Int(string formValue, int defaultValue = 0)
    {
        int result;
        return int.TryParse(formValue, out result) 
            ? result 
            : defaultValue;
    }

}

en daar ga je. Ik weet zeker dat je het bovenstaande kunt verbeteren, maar daarmee ga je door.

1
toegevoegd
Bedankt Patrick, ik zal dit gebruiken omdat het lijkt op de beste manier die ik kan vinden, maar het is teleurstellend dat er nog steeds sprake is van duplicatie. Er zijn nog steeds (in de kern eigenschappen) 3 lagen van het model. +1 voor het geven van de beste manier om dit te implementeren.
toegevoegd de auteur hofnarwillie, de bron

The way that I have always worked is that you have your Models e.g. Order & OrderLines which are where you store all your data and get hydrated either directly from the database by SQL or (more usually these days ) by an ORM such as NHibernate or Entity Framework.

U hebt dan ViewModels - deze worden gebruikt om de gegevens van uw toepassing naar de views te transporteren - ofwel rechtstreeks, dwz een sterk getypeerde weergave die gebonden is aan een OrderViewModel of via een actie die een JsonResult retourneert.

Een OrderViewModel is geen duplicaat van de Bestelling, het is ontworpen om alleen de gegevens vast te houden die op het scherm moeten worden gepresenteerd (als u veel verschillende weergaven hebt die een Bestelling op verschillende manieren weergeven, zou het perfect acceptabel kunnen zijn om veel verschillende ViewModels te hebben - één voor elke weergave die alleen de velden bevat die nodig zijn voor elke weergave). ViewModels mag ook geen complexe typen bevatten, behalve andere ViewModels. dit helpt om per ongeluk toegang tot gegevens te voorkomen (denk aan beveiliging en prestaties).

Dus gegeven

public class Order
{
    public int Id { get; set; }
    public DateTime OrderDate { get; set; }
    public User User { get; set; }
    public string  Description { get; set; }
    public List OrderLines { get; set; }
}

public class OrderLine
{
    public int Id { get; set; }
    public Order Order { get; set; }
    public String Description { get; set; }
    public int Quantity { get; set; }
    public int Weight { get; set; }
    public int Price { get; set; }
}

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Je zou de twee ViewModels kunnen hebben

public class OrderViewModel
{
    public int ID { get; set; }
    public List OrderLines { get; set; }
    public DateTime OrderDate { get; set; }
    public int UserId { get; set; }
    public string Description { get; set; }
}

public class OrderLineViewModel
{
    public int Id { get; set; }
    public int OrderId { get; set; }
    public String Description { get; set; }
    public int Quantity { get; set; }
    public int Weight { get; set; }
    public int Price { get; set; }
}

De weergavemodellen kunnen vervolgens naar behoefte worden geserialiseerd in JSON of worden gemarkeerd met validatiekenmerken, enzovoort.

0
toegevoegd
Bedankt Patrick, dat is nuttig. Maar hoe zit het met de json-versie ervan? Hoe maak ik een nieuw Order-object aan in JavaScript en stuur het in via een ajax-aanroep naar een asp.net webAPI? Ik zou ervoor moeten zorgen dat mijn javascript-prototype precies hetzelfde is als mijn viewmodel om de binding te laten werken?
toegevoegd de auteur hofnarwillie, de bron