2013-05-10 72 views
10

什么是加载最好的办法/滤波器/订购剑道网格以下类:如何使用Kendo UI Grid与ToDataSourceResult(),IQueryable <T>,ViewModel和AutoMapper?

域:

public class Car 
{ 
    public virtual int Id { get; set; } 
    public virtual string Name { get; set; } 
    public virtual bool IsActive { get; set; } 
} 

视图模型

public class CarViewModel 
{ 
    public virtual int Id { get; set; } 
    public virtual string Name { get; set; } 
    public virtual string IsActiveText { get; set; } 
} 

AutoMapper

Mapper.CreateMap<Car, CarViewModel>() 
     .ForMember(dest => dest.IsActiveText, 
       src => src.MapFrom(m => m.IsActive ? "Yes" : "No")); 

的IQueryable

var domainList = RepositoryFactory.GetCarRepository().GetAllQueryable(); 

DataSourceResult

var dataSourceResult = domainList.ToDataSourceResult<Car, CarViewModel>(request, 
          domain => Mapper.Map<Car, ViewModel>(domain)); 

网格

...Kendo() 
    .Grid<CarViewModel>() 
    .Name("gridCars") 
    .Columns(columns => 
    { 
    columns.Bound(c => c.Name); 
    columns.Bound(c => c.IsActiveText); 
    }) 
    .DataSource(dataSource => dataSource 
    .Ajax() 
    .Read(read => read.Action("ListGrid", "CarsController")) 
) 
    .Sortable() 
    .Pageable(p => p.PageSizes(true)) 

好的,电网负载PE rfectly首次,但是当我通过IsActiveText我收到以下消息过滤器/顺序:

无效的属性或字段 - “IsActiveText”类型:汽车

什么是最好的办法这种情况?

+0

什么是初始化网格的代码? – CodingWithSpike 2013-05-10 22:08:42

+0

我编辑了主题。 – rGiosa 2013-05-10 22:32:51

回答

4

有关这件事似乎很奇怪。你告诉剑道UI做一个网格CarViewModel

.Grid<CarViewModel>() 

,并告诉它有一个IsActive列:

public class CarViewModel 
{ 
    public virtual int Id { get; set; } 
    public virtual string Name { get; set; } 
    public virtual string IsActiveText { get; set; } 
} 

columns.Bound(c => c.IsActive); 

CarViewModel不叫这个名字有一个列

我的猜测是,剑道传递了CarViewModel IsActiveText的字段名称,但在运行的服务器上是ToDataSourceResult()针对Car对象(一个IQueryable<Car>),它没有该名称的属性。映射发生在筛选&排序后。

如果要过滤和排序在数据库中发生,那么在对数据库运行之前,需要在IQueryable上调用.ToDataSourceResult()

如果您已经取出所有Car记录了数据库,那么你可以先做你的映射,然后在一个IQueryable<CarViewModel>调用.ToDataSourceResult()解决这个问题。

+0

我编辑了这个主题,因为我在编写网格代码时犯了一个错误。我实际上显示了IsActiveText属性。我只是想知道是否有另一种方法可以使用AutoMapper对NHibernate和ToDataSourceResult()进行GetAllQueryable(),而不需要加载内存中的所有实体。 – rGiosa 2013-05-11 11:48:41

+0

'.ToDataSourceResult()'将表达式添加到调用它的Linq查询语句中。如果该查询尚未针对数据库执行,则过滤和排序将应用于数据库语句。 – CodingWithSpike 2013-05-12 16:43:01

+0

实际上,现在我再次查看您的类型,因为您将'Car.IsActive'定义为不是JavaScript类型的“bit”,所以在数据库上过滤“IsActive”将很困难。 'boolean'会更好,但即使如此,它也不匹配DB中的'string'。如果您希望'IsActive'使用'ToDataSourceResult'自动过滤数据库,那么您应该将其设置为一个字符串。否则,你必须自己编写一些代码来将'bit'转换为适当的'string'。 – CodingWithSpike 2013-05-12 16:47:36

10

我不喜欢Kendo实施“DataSourceRequestAttribute”和“DataSourceRequestModelBinder”的方式,但那是另一回事。

为了能够过滤/排序VM性能,这是 “扁平化” 的对象,试试这个:

领域模型:

public class Administrator 
{ 
    public int Id { get; set; } 

    public int UserId { get; set; } 

    public virtual User User { get; set; } 
} 

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

    public string UserName { get; set; } 

    public string Email { get; set; } 
} 

视图模型:

public class AdministratorGridItemViewModel 
{ 
    public int Id { get; set; } 

    [Displaye(Name = "E-mail")] 
    public string User_Email { get; set; } 

    [Display(Name = "Username")] 
    public string User_UserName { get; set; } 
} 

扩展:

public static class DataSourceRequestExtensions 
{ 
    /// <summary> 
    /// Enable flattened properties in the ViewModel to be used in DataSource. 
    /// </summary> 
    public static void Deflatten(this DataSourceRequest dataSourceRequest) 
    { 
     foreach (var filterDescriptor in dataSourceRequest.Filters.Cast<FilterDescriptor>()) 
     { 
      filterDescriptor.Member = DeflattenString(filterDescriptor.Member); 
     } 

     foreach (var sortDescriptor in dataSourceRequest.Sorts) 
     { 
      sortDescriptor.Member = DeflattenString(sortDescriptor.Member); 
     } 
    } 

    private static string DeflattenString(string source) 
    { 
     return source.Replace('_', '.'); 
    } 
} 

属性:

[AttributeUsage(AttributeTargets.Method)] 
public class KendoGridAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     base.OnActionExecuting(filterContext); 

     foreach (var sataSourceRequest in filterContext.ActionParameters.Values.Where(x => x is DataSourceRequest).Cast<DataSourceRequest>()) 
     { 
      sataSourceRequest.Deflatten(); 
     } 
    } 
} 

控制器的动作对Ajax数据加载:

[KendoGrid] 
public virtual JsonResult AdministratorsLoad([DataSourceRequestAttribute]DataSourceRequest request) 
    { 
     var administrators = this._administartorRepository.Table; 

     var result = administrators.ToDataSourceResult(
      request, 
      data => new AdministratorGridItemViewModel { Id = data.Id, User_Email = data.User.Email, User_UserName = data.User.UserName, }); 

     return this.Json(result); 
    } 
+0

很好的解决方案。在我看到你的解决方案之前,我尝试在我的依赖属性中使用'_',并认为kendo将“OF COURSE”绑定属性,但它没有。我想知道他们为什么没有实现这个逻辑.. – sotn 2014-02-17 11:07:20

+0

绝对辉煌,使用它与AutoMapper使用:http://stackoverflow.com/a/1630696/260556 – hokkos 2014-03-04 13:12:01

+0

位混淆...这只是映射的工作所需的对象,但排序/过滤呢?我可能会丢失一些东西 - 但是如果我尝试对列(映射到VM)进行排序,它会抛出一个错误,因为DataSourceRequest无法在模型上找到字段名称。 – pfeds 2015-06-10 03:32:22

2

FRANTISEK的解决方案是非常好的!但要注意将滤镜转换为FilterDescriptor。其中一些可以是复合的。

使用此实现DataSourceRequestExtensions代替弗朗齐歇克年代:

public static class DataSourceRequestExtensions 
{ 
    /// <summary> 
    /// Enable flattened properties in the ViewModel to be used in DataSource. 
    /// </summary> 
    public static void Deflatten(this DataSourceRequest dataSourceRequest) 
    { 
     DeflattenFilters(dataSourceRequest.Filters); 

     foreach (var sortDescriptor in dataSourceRequest.Sorts) 
     { 
      sortDescriptor.Member = DeflattenString(sortDescriptor.Member); 
     } 
    } 

    private static void DeflattenFilters(IList<IFilterDescriptor> filters) 
    { 
     foreach (var filterDescriptor in filters) 
     { 
      if (filterDescriptor is CompositeFilterDescriptor) 
      { 
       var descriptors 
        = (filterDescriptor as CompositeFilterDescriptor).FilterDescriptors; 
       DeflattenFilters(descriptors); 
      } 
      else 
      { 
       var filter = filterDescriptor as FilterDescriptor; 
       filter.Member = DeflattenString(filter.Member); 
      } 
     } 
    } 

    private static string DeflattenString(string source) 
    { 
     return source.Replace('_', '.'); 
    } 
} 
1

之一,如果你使用Telerik的数据访问或任何其他的IQueryable启用接口/ ORM您的数据来解决这个问题的好方法,是直接在创建视图您的数据库RDBMS将一对一(使用automapper)映射到您的视图模型。

  1. 创建列精确匹配视图模型属性名称要使用

    public class MyViewModelVM 
    { 
        public int Id { get; set; } 
        public string MyFlattenedProperty { get; set; } 
    } 
    
  2. 在你的SQL Server创建一个视图(或者任何你正在使用RDBMS)的视图模型,以及当然建立你的视图来查询正确的表格。请确保您在您的ORM类

    CREATE VIEW MyDatabaseView 
    AS 
    SELECT 
    t1.T1ID as Id, 
    t2.T2SomeColumn as MyFlattenedProperty 
    FROM MyTable1 t1 
    INNER JOIN MyTable2 t2 on t2.ForeignKeyToT1 = t1.PrimaryKey 
    
  3. 配置AutoMapper这个观点你的ORM视图类映射到您的视图模型

    Mapper.CreateMap<MyDatabaseView, MyViewModelVM>(); 
    
  4. 在你的剑道格读取动作,使用视图来构建查询,并利用投影ToDataSourceQueryResult Automapper

    public ActionResult Read([DataSourceRequest]DataSourceRequest request) 
    { 
        if (ModelState.IsValid) 
        { 
         var dbViewQuery = context.MyDatabaseView; 
    
         var result = dbViewQuery.ToDataSourceResult(request, r => Mapper.Map<MyViewModelVM>(r)); 
    
         return Json(result); 
        } 
    
        return Json(new List<MyViewModelVM>().ToDataSourceResult(request)); 
    } 
    

这是一个有点的开销,但它会帮助你在两个层面上的实现性能与大型数据集时:

  • 您正在使用本地RDBMS的观点,你可以调整自己。总是会胜过复杂的LINQ查询您构建在.NET中
  • 您可以利用过滤,分组的Telerik的ToDataSourceResult好处聚集,...
3

我跟着CodingWithSpike的建议和它的作品。我创建了DataSourceRequest类的扩展方法:

public static class DataSourceRequestExtensions 
    { 
     /// <summary> 
     /// Finds a Filter Member with the "memberName" name and renames it for "newMemberName". 
     /// </summary> 
     /// <param name="request">The DataSourceRequest instance. <see cref="Kendo.Mvc.UI.DataSourceRequest"/></param> 
     /// <param name="memberName">The Name of the Filter to be renamed.</param> 
     /// <param name="newMemberName">The New Name of the Filter.</param> 
     public static void RenameRequestFilterMember(this DataSourceRequest request, string memberName, string newMemberName) 
     { 
      foreach (var filter in request.Filters) 
      { 
       var descriptor = filter as Kendo.Mvc.FilterDescriptor; 
       if (descriptor.Member.Equals(memberName)) 
       { 
        descriptor.Member = newMemberName; 
       } 
      } 
     } 
    } 

然后在你的控制器中添加using来扩展类和调用ToDataSourceResult()之前,补充一点:

request.RenameRequestFilterMember("IsActiveText", "IsActive"); 
+0

正如其他答案中提到的,如果您有'CompositeFilterDescriptor' – 2016-09-29 19:01:51

1

我来了在同样的问题中,经过大量研究,我使用AutoMapper.QueryableExtensions库永久解决了这个问题。它有一个扩展方法,将您的实体查询投影到您的模型,之后,您可以在您的投影模型上应用ToDataSourceResult扩展方法。

public ActionResult GetData([DataSourceRequest]DataSourceRequest request) 
{ 
    IQueryable<CarModel> entity = getCars().ProjectTo<CarModel>(); 
    var response = entity.ToDataSourceResult(request); 
    return Json(response,JsonRequestBehavior.AllowGet); 
} 

记得使用CreateMap配置Automapper。

注意:此处getCars将返回IQueryable结果汽车。