使用以下类加载/过滤/订购Kendo网格的最佳方法是什么:
域
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))
好的,第一次网格加载完美,但是当我按IsActiveText
过滤/排序时,我收到以下消息:
无效的属性或字段 - 'IsActiveText'代表类型:Car
这种情况下最好的方法是什么?
答案 0 :(得分: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);
}
答案 1 :(得分:5)
关于这一点似乎很奇怪。您告诉Kendo UI为CarViewModel
.Grid<CarViewModel>()
并告诉它有一个IsActive
列:
columns.Bound(c => c.IsActive);
但CarViewModel
没有该名称的列:
public class CarViewModel
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string IsActiveText { get; set; }
}
我的猜测是,Kendo从CarViewModel IsActiveText
传递了字段名称,但在服务器上,您正在ToDataSourceResult()
对Car
个对象(IQueryable<Car>
)运行,没有该名称的属性。映射发生在过滤&amp;排序
如果您希望在数据库中进行过滤和排序,那么您需要在IQueryable上调用.ToDataSourceResult()
,然后才能对数据库运行。
如果您已从数据库中提取了所有Car
条记录,则可以先执行映射,然后在.ToDataSourceResult()
上调用IQueryable<CarViewModel>
来解决此问题。
答案 2 :(得分: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");
答案 3 :(得分:2)
František的解决方案非常好!但要小心将Filter转换为FilterDescriptor。其中一些可以是复合的。
使用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('_', '.');
}
}
答案 4 :(得分:2)
我遇到了同样的问题,经过大量研究后,我使用AutoMapper.QueryableExtensions库永久解决了这个问题。它有一个扩展方法,可以将实体查询投影到模型中,然后可以在投影模型上应用ToDataSourceResult扩展方法。
sails.middleware.policies.isauthenticated = function (req, res, next) {
req.user = { id: 1 };
next();
};
请记住使用CreateMap配置自动映射。
注意:这里getCars将返回IQueryable结果车。
答案 5 :(得分:1)
如果您对数据使用Telerik数据访问或任何其他可启用IQueryable的接口/ ORM,解决此问题的一种好方法是直接在数据库RDBMS中创建视图,该数据库将一对一(使用automapper)映射到您的viewmodel
创建您要使用的视图模型
public class MyViewModelVM
{
public int Id { get; set; }
public string MyFlattenedProperty { get; set; }
}
使用与viewmodel属性名称完全匹配的列在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
配置AutoMapper以将您的ORM视图类映射到您的viewmodel
Mapper.CreateMap<MyDatabaseView, MyViewModelVM>();
在您的Kendo网格读取操作中,使用视图构建查询,并使用Automapper投影ToDataSourceQueryResult
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));
}
它有点开销,但它可以帮助您在处理大型数据集时在两个级别上实现性能: