Domanda

What I'm attempting to do is have a "Super" entity class for saved products. Depending on where the product is in the application, certain extra attributes are necessary (but not always).

For example, when the product is being used within a grid, I want to have a ViewModel that derives from the Entity (inheriting all of the common fields) and then add a unique identifier attribute like "rowNumber" for easy searching by the kendo ui grid CRUD.

I thought I had all of this working, but I've hit a snag... Everything renders fine and operates correctly until I click "save" for the batch grid. It executes the function and all the data is present, but when it returns from the CRUD, it breaks. In firebug, I see that an exception is being thrown, but it never finishes (the ajax spinner stays there) and all information within the exception is empty...

I'm not sure if this is some issue with c# not playing well with CSLA or not. I'm unsure.

Any help would be appreciated! I can't upload images because my rep isn't high enough or else I would put a picture of the exception, but I'll at least put what appears in the Firebug console. Everything else about it is empty though...

Exception with endless execution and no response:

GET http://localhost:32574/Exception/SystemException/00000000-0000-0000-0000-000000000000

Entity: This file is auto-generated by a CodeSmith template so it is kind of nonsensical, but it holds field values that appear in the view (see below). The exception to what is on the view vs what is in the entity are fields that are not 'flattened' in the entity, as Kendo UI does not currently support this inside of editable grids.

ViewModel:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace Project.MVC.Models
{
    //The MetaData Class is where to put all annotations and validations
    [MetadataType(typeof(Project.Business.Shipment.ItemMetaDataClass))]
    public class ItemModel : Project.Business.Shipment.Item
    {
        public ItemModel()
        {
        }

        public long rowNumber { get; set; }

        public decimal Length { get; set; }
        public decimal Width { get; set; }
        public decimal Height { get; set; }

        [Display(Name = "UoMDim")]
        [UIHint("ItemGrid_RefUnitOfMeasurementListingDimension")]
        public string DimensionUnitOfMeasure { get; set; }

        [Display(Name = "UoMW")]
        [UIHint("ItemGrid_RefUnitOfMeasurementListingWeight")]
        public string WeightUnitOfMeasure { get; set; }

        [Display(Name = "Weight")]
        public decimal WeightValue { get; set; }

        [Display(Name = "Type")]
        [UIHint("ItemGrid_RefUnitTypeListing")]
        public string QuantityUnitOfMeasure { get; set; }

        [Display(Name = "Units")]
        public decimal QuantityValue { get; set; }
}
}

Grid Controller:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Kendo.Mvc.UI;
using Kendo.Mvc.Extensions;

namespace Project.MVC.Controllers
{
    [Authorize]
    public class ItemGridController : Csla.Web.Mvc.Controller
    {
        public ActionResult GetProducts([DataSourceRequest]DataSourceRequest request)
        {
            Project.MVC.Models.ShipmentModel shipmentModel = (Project.MVC.Models.ShipmentModel)ControllerBase.State.Object;

            return Json(shipmentModel.ItemModelList.ToDataSourceResult(request));
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult CreateProducts([DataSourceRequest]DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<Models.ItemModel> itemsToAdd)
        {
            Project.MVC.Models.ShipmentModel shipmentModel = (Project.MVC.Models.ShipmentModel)ControllerBase.State.Object;
            var results = new List<Models.ItemModel>();

            if (ModelState.IsValid)
            {
                foreach (Models.ItemModel newItem in itemsToAdd)
                {

                    if (shipmentModel.ItemModelList.Count > 0)
                    {
                        var nextID = (from i in shipmentModel.ItemModelList
                                      select i.rowNumber).Max() + 1;

                        newItem.rowNumber = nextID;
                    }

                    shipmentModel.ItemModelList.Add(newItem);
                    results.Add(newItem);
                }
            }

            return Json(results.ToDataSourceResult(request, ModelState));
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult UpdateProducts([DataSourceRequest]DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<Models.ItemModel> itemsToUpdate)
        {
            Project.MVC.Models.ShipmentModel shipmentModel = (Project.MVC.Models.ShipmentModel)ControllerBase.State.Object;
            var results = new List<Models.ItemModel>();

            foreach (var item in itemsToUpdate)
            {
                Models.ItemModel target = shipmentModel.ItemModelList.Find(i => i.rowNumber == item.rowNumber);
                if (target != null)
                {
                    target = item;
                }
            }

            return Json(ModelState.ToDataSourceResult());
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult DeleteProducts([DataSourceRequest]DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<Models.ItemModel> itemsToDelete)
        {
            Project.MVC.Models.ShipmentModel shipmentModel = (Project.MVC.Models.ShipmentModel)ControllerBase.State.Object;

            foreach (var item in itemsToDelete)
            {
                shipmentModel.ItemModelList.Remove(item);
            }

            return Json(ModelState.ToDataSourceResult());
        }
    }
}

View:

@model Project.MVC.Models.ShipmentModel
@using Kendo.Mvc.UI

@(Html.Kendo().Grid<Project.MVC.Models.ItemModel>()
    .Name("QuoteItemGrid")
    .Columns(columns =>
    {
        columns.Bound(i => i.FreightClass)
            .EditorTemplateName("ItemGrid_RefFreightClassListing")
            .Width(50);
        columns.Bound(i => i.Length).Width(30);
        columns.Bound(i => i.Width).Width(30);
        columns.Bound(i => i.Height).Width(30);
        columns.Bound(i => i.DimensionUnitOfMeasure)
            .EditorTemplateName("ItemGrid_RefUnitOfMeasurementListingDimension")
            .Width(50);
        columns.Bound(i => i.QuantityValue).Width(30);
        columns.Bound(i => i.QuantityUnitOfMeasure)
            .EditorTemplateName("ItemGrid_RefUnitTypeListing")
            .Width(50);
        columns.Bound(i => i.WeightValue).Width(30);
        columns.Bound(i => i.WeightUnitOfMeasure)
            .EditorTemplateName("ItemGrid_RefUnitOfMeasurementListingWeight")
            .Width(50);
        columns.Bound(i => i.NmfcCode).Width(50);
        columns.Bound(i => i.ItemDescription).Width(100);
        columns.Command(command =>
        {
            command.Destroy();
        }).Width(60);
    })
    .ToolBar(toolbar =>
    {
        toolbar.Create();
        toolbar.Save();
    })
    .Editable(editable => editable.Mode(GridEditMode.InCell).CreateAt(GridInsertRowPosition.Bottom))
    .Pageable()
    .Sortable()
    .Scrollable()
    .Resizable(resize => resize.Columns(true))
    .DataSource(dataSource => dataSource
        .Ajax()
        .Batch(true)
        .ServerOperation(false)
        .Events(events => events.Error("QuoteItemGrid_ErrorHandler"))
        .Model(model =>
        {
            model.Id(i => i.rowNumber);
            model.Field(i => i.DimensionUnitOfMeasure).DefaultValue("in");
            model.Field(i => i.WeightUnitOfMeasure).DefaultValue("lbs");
        })
        .Create(create => create.Action("CreateProducts", "ItemGrid"))
        .Read(read => read.Action("GetProducts", "ItemGrid"))
        .Update(update => update.Action("UpdateProducts", "ItemGrid"))
        .Destroy(destroy => destroy.Action("DeleteProducts", "ItemGrid"))
    )
)
È stato utile?

Soluzione 2

From an architectural perspective, my group ultimately thought it unwise to tie our database generated entities so closely to our view. Especially now that the project is being used in multiple places (.Net application, Webservices application, etc...).

So, in the end we ended up creating ViewModels for each entity so that we could be flexible with our entities across multiple platforms. This restructuring removed the issue above, as the entities are now never present within the view.

word of caution however: If you're using Kendo UI, you'll have to 'flatten' your view models that you want presented within a singular component that are deep objects (e.g. object within another object). At the moment, Kendo does not support this. We are using ValueInjecter.

Hopefully, this information will help someone else too.

Altri suggerimenti

I agree entirely. Ideally your CSLA .NET based objects are domain objects, not data objects, and so match the shape needed by the business requirements not by database tables or queries.

Sadly a lot of people end up (mis)using CSLA like an ORM and so have data-centric objects, thus missing a lot of the value of the framework, and necessitating the creation and maintenance of overly complex viewmodel types in addition to what (should have been) business types.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top