我一直在努力争取一段旧的(2011)C#代码,我从我的老板要我编辑的DLL中提取这些代码。
应用程序使用LINQ查询数据库,服务器端处理它并使用DataTable显示数据。 根据我收集的内容,编写它的人使用.NET Framework 4.0在Visual Studio 2010中创建了一个ASP.NET Web窗体站点。
他使用DataTables plugin和server-side parser from Zack Owens。 在我在VS 2017中重新创建的项目中,一切都很好,但是在运行时,一个错误来自他对DataTable解析器的小定制:其中, SelectProperties()函数已被完全重写从这个:
private Expression<Func<T, List<string>>> SelectProperties
{
get
{
return value => _properties.Select
(
// empty string is the default property value
prop => (prop.GetValue(value, new object[0]) ?? string.Empty).ToString()
)
.ToList();
}
}
到此:
private Expression<Func<T, List<string>>> SelectProperties
{
get
{
var parameterExpression = Expression.Parameter(typeof(T), "value");
// (Edited) The bug happens there: type_RD is null because GetMethod is not valid
var type_RD = typeof(Enumerable).GetMethod(
"ToList",
new Type[] {
typeof(IEnumerable<string>)
}
);
// The programs crashes there (System.Null.Exception)
var methodFromHandle = (MethodInfo)MethodBase.GetMethodFromHandle(
type_RD
.MethodHandle);
var expressionArray = new Expression[1];
var methodInfo = (MethodInfo)MethodBase.GetMethodFromHandle(
typeof(Enumerable).GetMethod("Select", new Type[] {
typeof(IEnumerable<PropertyInfo>),
typeof(Func<PropertyInfo, string>)
})
.MethodHandle);
var expressionArray1 = new Expression[] {
Expression.Field(
Expression.Constant(
this,
typeof(DataTableParser<T>)
),
FieldInfo.GetFieldFromHandle(
typeof(DataTableParser<T>).GetField("_properties").FieldHandle,
typeof(DataTableParser<T>).TypeHandle
)
), null
};
var parameterExpression1 = Expression.Parameter(
typeof(PropertyInfo),
"prop"
);
var methodFromHandle1 = (MethodInfo)MethodBase.GetMethodFromHandle(
typeof(PropertyInfo).GetMethod(
"GetValue",
new Type[] {
typeof(object),
typeof(object[])
}
)
.MethodHandle);
var expressionArray2 = new Expression[] {
Expression.Convert(
parameterExpression,
typeof(object)
),
Expression.NewArrayInit(
typeof(object),
new Expression[0]
)
};
var methodCallExpression = Expression.Call(
Expression.Coalesce(
Expression.Call(
parameterExpression1,
methodFromHandle1,
expressionArray2
),
Expression.Field(
null,
FieldInfo.GetFieldFromHandle(
typeof(string).GetField("Empty").FieldHandle
)
)
),
(MethodInfo)MethodBase.GetMethodFromHandle(
typeof(object).GetMethod("ToString").MethodHandle
),
new Expression[0]
);
expressionArray1[1] = Expression.Lambda<Func<PropertyInfo, string>>(
methodCallExpression,
parameterExpression1
);
expressionArray[0] = Expression.Call(
null,
methodInfo,
expressionArray1
);
// Return Lambda
return Expression.Lambda<Func<T, List<string>>>(
Expression.Call(
null,
methodFromHandle,
expressionArray
),
parameterExpression
);
}
}
我的问题:
我唯一的提示是,当我使用原始的SelectProperties代码时,数据位于错误的列中并且排序导致错误500“抛出类型'Antlr.Runtime.NoViableAltException'的异常”列。
以下是此自定义DataTable.cs解析器的完整代码:
提示:查找(已编辑)标签
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;
// (Edited) Source : Zack Owens commented https://github.com/garvincasimir/csharp-datatables-parser/issues/2#issuecomment-20441424
/* (Edited) Adapting to custom namespace
namespace DataTables
*/
namespace MyApp.Web.WebServices.Utils
{
/// <summary>
/// Parses the request values from a query from the DataTables jQuery plugin
/// </summary>
/// <typeparam name="T">List data type</typeparam>
public class DataTableParser<T>
{
/*
* int: iDisplayStart - Display start point
* int: iDisplayLength - Number of records to display
* string: string: sSearch - Global search field
* boolean: bEscapeRegex - Global search is regex or not
* int: iColumns - Number of columns being displayed (useful for getting individual column search info)
* string: sSortable_(int) - Indicator for if a column is flagged as sortable or not on the client-side
* string: sSearchable_(int) - Indicator for if a column is flagged as searchable or not on the client-side
* string: sSearch_(int) - Individual column filter
* boolean: bEscapeRegex_(int) - Individual column filter is regex or not
* int: iSortingCols - Number of columns to sort on
* int: iSortCol_(int) - Column being sorted on (you will need to decode this number for your database)
* string: sSortDir_(int) - Direction to be sorted - "desc" or "asc". Note that the prefix for this variable is wrong in 1.5.x, but left for backward compatibility)
* string: sEcho - Information for DataTables to use for rendering
*/
private const string INDIVIDUAL_SEARCH_KEY_PREFIX = "sSearch_";
private const string INDIVIDUAL_SORT_KEY_PREFIX = "iSortCol_";
private const string INDIVIDUAL_SORT_DIRECTION_KEY_PREFIX = "sSortDir_";
private const string DISPLAY_START = "iDisplayStart";
private const string DISPLAY_LENGTH = "iDisplayLength";
private const string ECHO = "sEcho";
private const string ASCENDING_SORT = "asc";
private const string OBJECT_DATA_PREFIX = "mDataProp_";
private IQueryable<T> _queriable;
private readonly HttpRequestBase _httpRequest;
private readonly Type _type;
private readonly PropertyInfo[] _properties;
public DataTableParser(HttpRequestBase httpRequest, IQueryable<T> queriable)
{
_queriable = queriable;
_httpRequest = httpRequest;
_type = typeof(T);
_properties = _type.GetProperties();
}
public DataTableParser(HttpRequest httpRequest, IQueryable<T> queriable)
: this(new HttpRequestWrapper(httpRequest), queriable) { }
/// <summary>
/// Parses the <see cref="HttpRequestBase"/> parameter values for the accepted
/// DataTable request values
/// </summary>
/// <returns>Formated output for DataTables, which should be serialized to JSON</returns>
/// <example>
/// In an ASP.NET MVC from a controller, you can call the Json method and return this result.
///
/// public ActionResult List()
/// {
/// // change the following line per your data configuration
/// IQueriable<User> users = datastore.Linq();
///
/// if (Request["sEcho"] != null) // always test to see if the request is from DataTables
/// {
/// var parser = new DataTableParser<User>(Request, users);
/// return Json(parser.Parse());
/// }
/// return Json(_itemController.CachedValue);
/// }
///
/// If you're not using MVC, you can create a web service and write the JSON output as such:
///
/// using System.Web.Script.Serialization;
/// public class MyWebservice : System.Web.Services.WebService
/// {
/// public string MyMethod()
/// {
/// // change the following line per your data configuration
/// IQueriable<User> users = datastore.Linq();
///
/// response.ContentType = "application/json";
///
/// JavaScriptSerializer serializer = new JavaScriptSerializer();
/// var parser = new DataTableParser<User>(Request, users);
/// return new JavaScriptSerializer().Serialize(parser.Parse());
/// }
/// }
/// </example>
public FormatedList<T> Parse()
{
var list = new FormatedList();
list.Import(_properties.Select(x => x.Name).ToArray());
list.sEcho = int.Parse(_httpRequest[ECHO]);
list.iTotalRecords = _queriable.Count();
ApplySort();
int skip = 0, take = 10;
int.TryParse(_httpRequest[DISPLAY_START], out skip);
int.TryParse(_httpRequest[DISPLAY_LENGTH], out take);
/* (Edited) This new syntax works well
list.aaData = _queriable.Where(ApplyGenericSearch)
.Where(IndividualPropertySearch)
.Skip(skip)
.Take(take)
.Select(SelectProperties)
.ToList();
list.iTotalDisplayRecords = list.aaData.Count;
*/
list.aaData = _queriable.Where(ApplyGenericSearch)
.Where(IndividualPropertySearch)
.Skip(skip)
.Take(take)
.ToList()
.AsQueryable()
.Select(SelectProperties)
.ToList();
list.iTotalDisplayRecords = list.iTotalRecords;
return list;
}
private void ApplySort()
{
// (Edited) Added one line, see after
bool firstSort = true;
foreach (string key in _httpRequest.Params.AllKeys.Where(x => x.StartsWith(INDIVIDUAL_SORT_KEY_PREFIX)))
{
int sortcolumn = int.Parse(_httpRequest[key]);
if (sortcolumn < 0 || sortcolumn >= _properties.Length)
break;
string sortdir = _httpRequest[INDIVIDUAL_SORT_DIRECTION_KEY_PREFIX + key.Replace(INDIVIDUAL_SORT_KEY_PREFIX, string.Empty)];
var paramExpr = Expression.Parameter(typeof(T), "val");
/* Edited as per https://stackoverflow.com/a/8974875/5426777 and mentioned here too https://weblogs.asp.net/zowens/jquery-datatables-plugin-meets-c#after-content
var propertyExpr = Expression.Lambda<Func<T, object>>(Expression.Property(paramExpr, _properties[sortcolumn]), paramExpr);
*/
var expression = Expression.Convert(Expression.Property(paramExpr, _properties[sortcolumn]),typeof(object));
var propertyExpr = Expression.Lambda<Func<T, object>>(expression, paramExpr);
/* Edited cf. https://weblogs.asp.net/zowens/jquery-datatables-plugin-meets-c#after-content
* Correcting multi-sort errors
if (string.IsNullOrEmpty(sortdir) || sortdir.Equals(ASCENDING_SORT, StringComparison.OrdinalIgnoreCase))
_queriable = _queriable.OrderBy(propertyExpr);
else
_queriable = _queriable.OrderByDescending(propertyExpr);
*/
if (firstSort)
{
if (string.IsNullOrEmpty(sortdir) || sortdir.Equals(ASCENDING_SORT, StringComparison.OrdinalIgnoreCase))
_queriable = _queriable.OrderBy(propertyExpr);
else
_queriable = _queriable.OrderByDescending(propertyExpr);
firstSort = false;
}
else
{
if (string.IsNullOrEmpty(sortdir) || sortdir.Equals(ASCENDING_SORT, StringComparison.OrdinalIgnoreCase))
_queriable = ((IOrderedQueryable<T>)_queriable).ThenBy(propertyExpr);
else
_queriable = ((IOrderedQueryable<T>)_queriable).ThenByDescending(propertyExpr);
}
}
}
/// <summary>
/// Expression that returns a list of string values, which correspond to the values
/// of each property in the list type
/// </summary>
/// <remarks>This implementation does not allow indexers</remarks>
private Expression<Func<T, List<string>>> SelectProperties
{
get
{
/* (Edited) This is the edit that does not work and that I don't understand
return value => _properties.Select
(
// empty string is the default property value
prop => (prop.GetValue(value, new object[0]) ?? string.Empty).ToString()
)
.ToList();
*/
var parameterExpression = Expression.Parameter(typeof(T), "value");
// (Edited) The bug happens there: type_RD is null because GetMethod is not valid
var type_RD = typeof(Enumerable).GetMethod(
"ToList",
new Type[] {
typeof(IEnumerable<string>)
}
);
// The programs crashes there (System.Null.Exception)
var methodFromHandle = (MethodInfo)MethodBase.GetMethodFromHandle(
type_RD
.MethodHandle);
var expressionArray = new Expression[1];
var methodInfo = (MethodInfo)MethodBase.GetMethodFromHandle(
typeof(Enumerable).GetMethod("Select", new Type[] {
typeof(IEnumerable<PropertyInfo>),
typeof(Func<PropertyInfo, string>)
})
.MethodHandle);
var expressionArray1 = new Expression[] {
Expression.Field(
Expression.Constant(
this,
typeof(DataTableParser<T>)
),
FieldInfo.GetFieldFromHandle(
typeof(DataTableParser<T>).GetField("_properties").FieldHandle,
typeof(DataTableParser<T>).TypeHandle
)
), null
};
var parameterExpression1 = Expression.Parameter(
typeof(PropertyInfo),
"prop"
);
var methodFromHandle1 = (MethodInfo)MethodBase.GetMethodFromHandle(
typeof(PropertyInfo).GetMethod(
"GetValue",
new Type[] {
typeof(object),
typeof(object[])
}
)
.MethodHandle);
var expressionArray2 = new Expression[] {
Expression.Convert(
parameterExpression,
typeof(object)
),
Expression.NewArrayInit(
typeof(object),
new Expression[0]
)
};
var methodCallExpression = Expression.Call(
Expression.Coalesce(
Expression.Call(
parameterExpression1,
methodFromHandle1,
expressionArray2
),
Expression.Field(
null,
FieldInfo.GetFieldFromHandle(
typeof(string).GetField("Empty").FieldHandle
)
)
),
(MethodInfo)MethodBase.GetMethodFromHandle(
typeof(object).GetMethod("ToString").MethodHandle
),
new Expression[0]
);
expressionArray1[1] = Expression.Lambda<Func<PropertyInfo, string>>(
methodCallExpression,
parameterExpression1
);
expressionArray[0] = Expression.Call(
null,
methodInfo,
expressionArray1
);
// Return Lambda
return Expression.Lambda<Func<T, List<string>>>(
Expression.Call(
null,
methodFromHandle,
expressionArray
),
parameterExpression
);
}
}
/// <summary>
/// Compound predicate expression with the individual search predicates that will filter the results
/// per an individual column
/// </summary>
private Expression<Func<T, bool>> IndividualPropertySearch
{
get
{
var paramExpr = Expression.Parameter(typeof(T), "val");
Expression whereExpr = Expression.Constant(true); // default is val => True
List<Expression> le = new List<Expression>() { whereExpr };
List<ParameterExpression> lp = new List<ParameterExpression>() { paramExpr };
foreach (string key in _httpRequest.Params.AllKeys.Where(x => x.StartsWith(INDIVIDUAL_SEARCH_KEY_PREFIX)))
{
var mDataProp = key.Replace(INDIVIDUAL_SEARCH_KEY_PREFIX, OBJECT_DATA_PREFIX);
if (string.IsNullOrEmpty(_httpRequest[key]) || string.IsNullOrEmpty(_httpRequest[mDataProp]))
{
continue; // ignore if the option is invalid
}
var f = _properties.First(p => p.Name == _httpRequest[mDataProp]);
string query = _httpRequest[key].ToLower();
MethodCallExpression mce;
if (f.PropertyType != typeof(string))
{
// val.{PropertyName}.ToString().ToLower().Contains({query})
mce = Expression.Call(Expression.Call(Expression.Property(paramExpr, f), "ToString", new Type[0]), typeof(string).GetMethod("ToLower", new Type[0]));
}
else
{
mce = Expression.Call(Expression.Property(paramExpr, f), typeof(string).GetMethod("ToLower", new Type[0]));
}
// reset where expression to also require the current constraint
whereExpr = Expression.And(whereExpr, Expression.Call(mce, typeof(string).GetMethod("Contains"), Expression.Constant(query)));
le.Add(whereExpr);
}
var agg = le.Aggregate((prev, next) => Expression.And(prev, next));
return Expression.Lambda<Func<T, bool>>(agg, paramExpr);
}
}
/// <summary>
/// Expression for an all column search, which will filter the result based on this criterion
/// </summary>
private Expression<Func<T, bool>> ApplyGenericSearch
{
get
{
string search = _httpRequest["sSearch"];
// default value
if (string.IsNullOrEmpty(search) || _properties.Length == 0)
return x => true;
// invariant expressions
var searchExpression = Expression.Constant(search.ToLower());
var paramExpression = Expression.Parameter(typeof(T), "val");
// query all properties and returns a Contains call expression
// from the ToString().ToLower()
var propertyQuery = (from property in _properties
let tostringcall = Expression.Call(
Expression.Call(
Expression.Property(paramExpression, property), "ToString", new Type[0]),
typeof(string).GetMethod("ToLower", new Type[0]))
select Expression.Call(tostringcall, typeof(string).GetMethod("Contains"), searchExpression)).ToArray();
// we now need to compound the expression by starting with the first
// expression and build through the iterator
Expression compoundExpression = propertyQuery[0];
// add the other expressions
for (int i = 1; i < propertyQuery.Length; i++)
compoundExpression = Expression.Or(compoundExpression, propertyQuery[i]);
// compile the expression into a lambda
return Expression.Lambda<Func<T, bool>>(compoundExpression, paramExpression);
}
}
}
public class FormatedList<T>
{
public FormatedList()
{
}
public int sEcho { get; set; }
public int iTotalRecords { get; set; }
public int iTotalDisplayRecords { get; set; }
public List<T> aaData { get; set; }
public string sColumns { get; set; }
public void Import(string[] properties)
{
sColumns = string.Empty;
for (int i = 0; i < properties.Length; i++)
{
sColumns += properties[i];
if (i < properties.Length - 1)
sColumns += ",";
}
}
}
}
我是C#,.NET,Linq等的初学者,虽然我知道很多其他语言 设置:Windows 7 64,Visual Studio 2017。
感谢您的帮助!
答案 0 :(得分:0)
TL; DR:使用原始解析器的SelectProperties函数。
好的,我解决了这部分问题 我的问题是我使用从DLL中提取的分解代码。 .NET Reflector和JustDecompile都提供了非常繁重的代码,尽管它只是原始代码的一个糟糕的反编译。
最后,我明白我的“错误列中的数据”和“错误500 - 类型'Antlr.Runtime.NoViableAltException'的异常被抛出”的问题并非来自此部分代码。
以下是使用Zack Owen的DataTableParser对服务器端处理数据中的错误数据的解释,因为在使用JustDecompile或.NET Reflector进行反编译时,实体框架POCO中的属性按字母顺序重新排序:
https://forum.red-gate.com/discussion/80861/bugs-of-field-order-in-reflector#Comment_149618