Search code examples
asp.net-mvc-5infragisticsignite-uiiggrid

Why does Ignite UI Hierarchical Grid throw a NullReferenceException when I add a layout?


Whenever I load the grid without adding a layout the parent grid loads just fine. However, whenever I add the layout, A NullReferenceException is thrown when binding data to the parent.

public ActionResult Index()
    {
        GridModel grid = GridLoadOnDemandModel();
        grid.ID = "Services";
        grid.LoadOnDemand = true;
        grid.DataSourceUrl = this.Url.Action("BindParent");
        grid.ColumnLayouts[0].DataSourceUrl = this.Url.Action("BindChild");
        return View(grid);
    }

    private GridModel GridLoadOnDemandModel()
    {
        GridModel grid = new GridModel();
        grid.AutoGenerateLayouts = false;
        grid.AutoGenerateColumns = true;
        grid.PrimaryKey = "ID";
        grid.Width = "100%";
        grid.Columns.Add(new GridColumn() { HeaderText = "Service ID", Key = "ID", DataType = "number" });
        grid.Columns.Add(new GridColumn() { HeaderText = "Client ID", Key = "ClientID", DataType = "number" });
        grid.Columns.Add(new GridColumn() { HeaderText = "Package ID", Key = "PackageID", DataType = "number" });

        GridColumnLayoutModel layout = new GridColumnLayoutModel();
        layout.Key = "ServiceHistory";
        layout.ForeignKey = "ID";
        layout.PrimaryKey = "ID";
        layout.AutoGenerateColumns = false;
        layout.Columns.Add(new GridColumn() { HeaderText = "Log ID", Key = "LogID", DataType = "number", Hidden = true });
        layout.Columns.Add(new GridColumn() { HeaderText = "Service ID", Key = "ID", DataType = "number", Hidden = true});
        layout.Columns.Add(new GridColumn() { HeaderText = "Client ID", Key = "ClientID", DataType = "number" });
        layout.Columns.Add(new GridColumn() { HeaderText = "Package ID", Key = "PackageID", DataType = "number" });

        layout.Width = "100%";
        grid.ColumnLayouts.Add(layout);

        return grid;
    }

The error is thrown in the BindParent method on the GetData method.

    public JsonResult BindParent()
    {
        GridModel model = GridLoadOnDemandModel();
        model.DataSource = GetService().AsQueryable();
        return model.GetData();
    }

    public JsonResult BindChild(string path, string layout)
    {
        GridModel model = GridLoadOnDemandModel();
        model.DataSource = GetServiceLogs().AsQueryable();
        return model.GetData(path, layout);
    }

    private IEnumerable<Service> GetService()
    {
        var services = new List<Service>();

        for (int i = 0; i < 10; i++)
        {
            var service = new Service
            {
                ID = i,
                ClientID = 1,
                PackageID = 1
            };

            services.Add(service);
        }

        return services;
    }

    private IEnumerable<ServiceLog> GetServiceLogs()
    {
        var serviceLogs = new List<ServiceLog>();

        for (int i = 0; i < 10; i++)
        {
            var serviceLog = new ServiceLog
            {
                LogID = i,
                ID = 1,
                ClientID = i,
                PackageID = i
            };

            serviceLogs.Add(serviceLog);
        }

        return serviceLogs;
    } 
public class Service
{
    public int ID { get; set; }
    public int ClientID { get; set; }
    public int PackageID { get; set; }
}

public class ServiceLog
{
    public int LogID { get; set; }
    public int ID { get; set; }
    public int ClientID { get; set; }
    public int PackageID { get; set; }
}

public class Service
{
    public int ID { get; set; }
    public int ClientID { get; set; }
    public int PackageID { get; set; }
}

public class ServiceLog
{
    public int LogID { get; set; }
    public int ID { get; set; }
    public int ClientID { get; set; }
    public int PackageID { get; set; }
}

Here is the stack trace:

at Infragistics.Web.Mvc.GridModel.RenderHierarchicalQueryableRecursive(IQueryable queryable, WrappedGridResponse response, GridModel baseLayout) at Infragistics.Web.Mvc.GridModel.RenderHierarchicalQueryable(IQueryable queryable) at Infragistics.Web.Mvc.GridModel.DataBind() at Infragistics.Web.Mvc.GridModel.GetData() at IgniteUIHTMLEditorExample.Controllers.ServiceHistoryController.BindParent() in c:\Users\kyle.wingate\Documents\Visual Studio 2013\Projects\IgniteUIHTMLEditorExample\IgniteUIHTMLEditorExample\Controllers\ServiceHistoryController.cs:line 53 at lambda_method(Closure , ControllerBase , Object[] ) at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters) at System.Web.Mvc.Async.AsyncControllerActionInvoker.ActionInvocation.InvokeSynchronousActionMethod() at System.Web.Mvc.Async.AsyncControllerActionInvoker.b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult2.CallEndDelegate(IAsyncResult asyncResult) at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase1.End() at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag) at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.b__3d() at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.b__3f()


Solution

  • In the Service class/model you should have a property named after the GridColumnLayoutModel.Key value. Here is how the Service class should look:

    public class Service
    {
        public int ID { get; set; }
        public int ClientID { get; set; }
        public int PackageID { get; set; }
        public IEnumerable<ServiceLog>  ServiceHistory { get; set; }
    }
    

    This change will resolve the NullReferenceException, but there are some more changes that you need to make so that your sample runs as expected.

    You should also correctly configure the GridColumnLayoutModel.PrimaryKey and GridColumnLayoutModel.ForeignKey, because the GridModel.GetData depends on them to extract/filter the correct child records. I guess in your case the configuration for the ServiceHistory layout should look like this:

    //… code omitted
    layout.ForeignKey = "ID";
    layout.PrimaryKey = "LogID";
    // … code omitted
    

    With this change the GridModel.GetData method will filter the data by the ID property in the ServiceLog class.

    You may need to change the GetServiceLogs method to generate different ID, because right now it will return data only for Service with ID = 1