hofnarwillie
hofnarwillie

Reputation: 3660

MVC model duplication

I'm very new to ASP.NET MVC, so forgive me if this is something I should know. I haven't seen any obvious documentation on it, so here goes:

I have a LINQ to Entities data model and a MVC project. I use a lot of javascript/jquery, so have opted to access my data from the client through a WebAPI as json objects. However, I don't want to pass all the entity object properties though to the client, so I have added separate models to my MVC project in which I handle MVC model validation and Binding to my Views. Also, in order to work with it in my jquery, I have created json versions of the models.

This is only the start of the project and I don't want to start it off on the wrong foot. Having three versions of my models for each entity in my business layer is going to be a nightmare! I am sure that the overall structure of my project is a very common one, but can't see many developers settling for such duplication of code. There must be a better way of implementing it.

Any thoughts? Really appreciate any input.

Upvotes: 0

Views: 240

Answers (2)

Pat Hastings
Pat Hastings

Reputation: 412

In answer to your comment above - you can create your javascript viewmodel as a standard js object. I tend to use Knockout.js so it would look like this:

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

and then use an ajax post method to post it to your MVC action

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

Create your MVC action so that it accepts a forms collection as so:

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

and then you create your MVC viewmodel from the forms collection (i tend to do this by passing in the forms collection as a constructor method to the 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"]);

    }

}

As a null int in your javascript VM will lead to a string of 'undefined' being passed you need a converter method to safely extract non strings.

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

}

and there you go. I am sure you can improve on the above but that will get you going.

Upvotes: 1

Pat Hastings
Pat Hastings

Reputation: 412

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.

You then have ViewModels - these are used to transport the data from your application to the views - either directly ie a strongly typed view bound to say an OrderViewModel or via an action returning a JsonResult.

A OrderViewModel is not a duplication of Order as it is designed to only hold the data that is needed to be presented on the screen (If you have many different views displaying an Order in different ways it could be perfectly acceptable to have many different ViewModels -one for each view containing only the fields needed for each view). ViewModels should also not contain any complex types except other ViewModels. this helps keep accidental data access out of the views (think security and performance).

So Given

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

You could have the two ViewModels

public class OrderViewModel
{
    public int ID { get; set; }
    public List<OrderLineViewModel> 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; }
}

The view models could then be serialized into JSON as needed or marked up with validation attributes etc.

Upvotes: 0

Related Questions