C# 如何将Kendo UI网格与ToDataSourceResult()一起使用,IQueryable<;T>;,ViewModel和AutoMapper?
加载/过滤/订购具有以下类别的剑道网格的最佳方法是什么: 域:C# 如何将Kendo UI网格与ToDataSourceResult()一起使用,IQueryable<;T>;,ViewModel和AutoMapper?,c#,asp.net-mvc,kendo-ui,kendo-grid,C#,Asp.net Mvc,Kendo Ui,Kendo Grid,加载/过滤/订购具有以下类别的剑道网格的最佳方法是什么: 域: 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
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"));
数据源结果
var dataSourceResult = domainList.ToDataSourceResult<Car, CarViewModel>(request,
domain => Mapper.Map<Car, ViewModel>(domain));
var dataSourceResult=domainList.ToDataSourceResult(请求,
domain=>Mapper.Map(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))
…剑道()
.Grid()
.名称(“gridCars”)
.列(列=>
{
columns.Bound(c=>c.Name);
columns.Bound(c=>c.IsActiveText);
})
.DataSource(DataSource=>DataSource
.Ajax()
.Read(Read=>Read.Action(“ListGrid”、“CarsController”))
)
.Sortable()
.Pageable(p=>p.pagesize(真))
好的,网格第一次完全加载,但是当我通过IsActiveText
过滤/排序时,我得到以下消息:
类型为Car的属性或字段“IsActiveText”无效
在这种情况下,最好的方法是什么?这似乎有些奇怪。你告诉剑道用户界面为
CarViewModel
.Grid<CarViewModel>()
但是CarViewModel
没有该名称的列:
public class CarViewModel
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string IsActiveText { get; set; }
}
我猜Kendo是从CarViewModelIsActiveText
传递字段名的,但是在服务器上,您针对Car
对象运行ToDataSourceResult()
,而这些对象没有该名称的属性。映射发生在过滤和排序之后
如果希望在数据库中进行筛选和排序,则需要在iQueryTable对DB运行之前,在iQueryTable上调用.ToDataSourceResult()
如果您已经从数据库中取出了所有的
Car
记录,那么您可以通过先进行映射,然后在IQueryable
上调用.ToDataSourceResult()
来解决这个问题。我不喜欢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('_', '.');
}
}
František的解决方案非常好!但要小心将过滤器投射到过滤器描述器。其中一些可以是复合的 使用此DataSourceRequestExtensions实现,而不是František的:
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('_', '.');
}
}
公共静态类DataSourceRequestExtensions
{
///
///在ViewModel中启用要在数据源中使用的展开属性。
///
公共静态无效Deflatten(此DataSourceRequest DataSourceRequest)
{
DeflattenFilters(dataSourceRequest.Filters);
foreach(dataSourceRequest.Sorts中的var sortddescriptor)
{
sortDescriptor.Member=DeflattenString(sortDescriptor.Member);
}
}
专用静态空隙放气过滤器(IList过滤器)
{
foreach(过滤器中的var filterDescriptor)
{
if(filterDescriptor是CompositeFilterDescriptor)
{
变量描述符
=(filterDescriptor作为CompositeFilterDescriptor)。filterDescriptor;
放气过滤器(描述符);
}
其他的
{
var filter=filterDescriptor作为filterDescriptor;
filter.Member=DeflattenString(filter.Member);
}
}
}
私有静态字符串DeflattenString(字符串源)
{
返回源。替换(“”,“);
}
}
如果您在数据上使用Telerik Data Access或任何其他支持IQueryable的接口/ORM,解决此问题的一个好方法是直接在数据库RDBMS中创建视图,将一对一(使用automapper)映射到您的viewmodel
public class MyViewModelVM
{
public int Id { get; set; }
public string MyFlattenedProperty { get; set; }
}
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
Mapper.CreateMap<MyDatabaseView, MyViewModelVM>();
Mapper.CreateMap();
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));
}
公共操作结果读取([DataSourceRequest]DataSourceRequest请求)
{
if(ModelState.IsValid)
{
var dbViewQuery=context.MyDatabaseView;
var result=dbViewQuery.ToDataSourceResult(请求,r=>Mapper.Map(r));
返回Json(结果);
}
返回Json(new List().ToDataSourceResult(request));
}
- 您使用的是本机RDBMS视图,您可以自己进行调优。将始终优于您在.NET中构建的复杂LINQ查询
- 您可以利用Telerik ToDataSourceResult的过滤、分组、聚合等优点
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;
}
}
}
}
我遇到了同样的问题,经过大量研究,我使用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);
}
public ActionResult GetData([DataSourceRequest]DataSourceRequest请求)
{
IQueryable实体=getCars().ProjectTo();
var response=entity.ToDataSourceResult(请求);
返回Json(response,JsonRequestBehavior.AllowGet);
}
请记住使用CreateMap配置AutoMaper
注意:此处getCars将返回IQueryable结果car。初始代码是什么
Mapper.CreateMap<MyDatabaseView, MyViewModelVM>();
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));
}
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;
}
}
}
}
request.RenameRequestFilterMember("IsActiveText", "IsActive");
public ActionResult GetData([DataSourceRequest]DataSourceRequest request)
{
IQueryable<CarModel> entity = getCars().ProjectTo<CarModel>();
var response = entity.ToDataSourceResult(request);
return Json(response,JsonRequestBehavior.AllowGet);
}