/// <summary> /// Creates a DataTable /// </summary> /// <typeparam name="TModel"></typeparam> /// <param name="htmlHelper"></param> /// <param name="source"></param> /// <param name="columns"></param> /// <param name="dialogs"></param> /// <param name="actions"> </param> /// <param name="add"> </param> /// <param name="defaultCount"> </param> /// <param name="defaultSort"> </param> /// <returns></returns> public static MvcHtmlString DataTableFor <TModel, TEntity>( this HtmlHelper <TModel> htmlHelper, IQueryable <TEntity> source, ColumnCollection <TEntity> columns, DataTablesDialogModelCollection dialogs = null, DataTablesDirectActionModelCollection actions = null, DataTablesAddModel add = null, int?defaultCount = null, List <DataTablesRequestModel.Sort> defaultSort = null, object htmlAttributes = null, string ajaxUrl = null) { var attributeDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); // get ajax url from calling Action route values // this is used in the generation of the tableId and will help make it unique to the page but static to the application if (String.IsNullOrEmpty(ajaxUrl)) { ajaxUrl = new UrlHelper(htmlHelper.ViewContext.RequestContext).RouteUrl(htmlHelper.ViewContext.RouteData.Values); } string tableId; if (attributeDictionary.ContainsKey("id")) { tableId = attributeDictionary["id"].ToString(); } else { // Generate a table ID that is unique for the table. It must be unique in the page // but the same on every request. This ID is appended to the AJAX callback URL so // the controller that handles the request can determin which table is make a request. tableId = ajaxUrl + source.GetType().AssemblyQualifiedName + String.Join(",", columns.Select(x => x.ColumnName)); tableId = 'T' + String.Join("", System.Security.Cryptography.MD5.Create().ComputeHash( System.Text.Encoding.ASCII.GetBytes(tableId)).Select( x => x.ToString("X2"))); attributeDictionary.Add("id", tableId); } // merge classes if (attributeDictionary.ContainsKey("class")) { attributeDictionary["class"] += " dataTable"; } else { attributeDictionary.Add("class", "dataTable"); } // Create the AJAX callback URL. It will be the original URL for the current // request, keeping all parameters intact, with the table ID appended. Carefull // to create proper format. // add query string and datatableid if (htmlHelper.ViewData.ModelState.Count > 0) { // combine route values and model state to get the needed url to access the path this method was called from ajaxUrl += '?' + String.Join("&", htmlHelper.ViewData.ModelState.Where( x => x.Value.Errors.Count == 0 && x.Value.Value != null).Select( x => x.Key + "=" + HttpUtility.UrlEncode(x.Value.Value.AttemptedValue))); } ajaxUrl = string.Format("{0}{1}epicTableId={2}", ajaxUrl, ajaxUrl.Contains("?") ? "&" : "?", tableId); // set up initialization model var dataTableInit = new DataTablesInitializationModel <TEntity> { Columns = columns, Url = ajaxUrl, Source = source, ID = tableId, Dialogs = dialogs, DirectActions = actions, Add = add, DefaultCount = defaultCount, DefaultSorts = defaultSort, TableAttributes = attributeDictionary, }; // Save it in ViewData where the controller can get it. Since there may be more than // one table in a page, this must be managed as a collection. // // NOTE: Using htmlHelper.ViewData doesn't work. That doesn't seem to be visible to the // controller. So we use the one that is a property of the Controller. Documentation // seems to indicate they are the same, but they don't appear to be. // This could be due to the viewData not being passed around to child actions var viewData = htmlHelper.ViewContext.Controller.ViewData; var models = viewData["DataTablesModels"] as Dictionary <string, object>; if (models == null) { models = new Dictionary <string, object>(); viewData.Add("DataTablesModels", models); } models.Add(tableId, dataTableInit); // return html grid and javascript initializer using (var sw = new StringWriter()) { // create a view context for the DataTablesPartial var newViewContext = new ViewContext(htmlHelper.ViewContext, htmlHelper.ViewContext.View, new ViewDataDictionary(dataTableInit), htmlHelper.ViewContext.TempData, sw); var view = ViewEngines.Engines.FindPartialView(htmlHelper.ViewContext.Controller.ControllerContext, "_DataTablesPartial"); // render the View to a string to outputting to the page view.View.Render(newViewContext, sw); return(MvcHtmlString.Create(sw.GetStringBuilder().ToString())); } }
/// <summary> /// The call for this function is generated in order to simplify the filtering below. This is not usually necessary for IQueryable<object> /// However, LLBLGen has problems with IQuerable when the type is casted to object, so the proper typed call is generated above and this generic is called. /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="dataTableInit"></param> /// <param name="model"></param> /// <returns></returns> private static JsonResult Query <TEntity>(DataTablesInitializationModel <TEntity> dataTableInit, DataTablesRequestModel model) { // Get source model used to create the view. var source = dataTableInit.Source; // get IQueryable type of object passed in, this makes the work below generalized for any type /*var source2 = source as IQueryable<PatientEntity>; * if(source2 != null) * { * source2 = source2.Where(x => Convert.ToString(x.BirthDate, new DateTimeFormatInfo(){ShortDatePattern = }).Contains("fe")); * * var result = source2.ToList(); * } */ // start with the source, then add on each filter var count = source.Count(); var displayCount = count; if (!String.IsNullOrEmpty(model.sSearch)) { var llblGenProProvider = source.Provider as LLBLGenProProvider; if (llblGenProProvider != null) { var dateConverter = new FunctionMapping(typeof(DateTime), "ToString", 0, @"CONVERT(nvarchar(2), CONVERT(int, SUBSTRING(CONVERT(nvarchar(23), {0}, 121), 6, 2))) + '/' + CONVERT(nvarchar(2), CONVERT(int, SUBSTRING(CONVERT(nvarchar(23), {0}, 121), 9, 2))) + '/' + CONVERT(nvarchar(4), CONVERT(int, SUBSTRING(CONVERT(nvarchar(23), {0}, 121), 1, 4))) + ' ' + CASE WHEN CONVERT(int, SUBSTRING(CONVERT(nvarchar(23), {0}, 121), 12, 2)) > 12 THEN CONVERT(nvarchar(4), CONVERT(int, SUBSTRING(CONVERT(nvarchar(23), {0}, 121), 12, 2)) - 12) ELSE CONVERT(nvarchar(4), CONVERT(int, SUBSTRING(CONVERT(nvarchar(23), {0}, 121), 12, 2))) END +':' +SUBSTRING(CONVERT(nvarchar(23), {0}, 121), 15, 2) + ':' + SUBSTRING(CONVERT(nvarchar(23), {0}, 121), 18, 2) + CASE WHEN CONVERT(int, SUBSTRING(CONVERT(nvarchar(23), {0}, 121), 12, 2)) > 11 THEN ' PM' ELSE ' AM' END"); //var indexOf = new FunctionMapping(typeof(string), "IndexOf", 1, "PATINDEX('%{1}%', {0}) - 1"); //var indexOf2 = new FunctionMapping(typeof(string), "IndexOf", 2, "PATINDEX('%{1}%', {0}) - 1"); if ((llblGenProProvider).CustomFunctionMappings == null) { (llblGenProProvider).CustomFunctionMappings = new FunctionMappingStore(); } (llblGenProProvider).CustomFunctionMappings.Add(dateConverter); //(llblGenProProvider).CustomFunctionMappings.Add(indexOf); //(llblGenProProvider).CustomFunctionMappings.Add(indexOf2); // ^--- Doesn't work for no apparent reason but would make the Search() function a lot simpler. } // search all specified columns for the specified string source = source.Search(dataTableInit.Columns, model.sSearch, !(source.Provider is LLBLGenProProvider)); displayCount = source.Count(); } // sort the result based on the model, this calls OrderBy on multiple columns sequentially if (model.Sorts.Any()) { source = source.Sort(dataTableInit.Columns, model.Sorts); } // filter page based on start and length, this is like the SQL LIMIT directive if (model.iDisplayLength > 0) { source = source.Skip(model.iDisplayStart).Take(model.iDisplayLength); } // now build the response var response = new DataTablesResponseModel { sEcho = model.sEcho.Value, iTotalRecords = count, iTotalDisplayRecords = displayCount }; // loop through each item and output JSON foreach (TEntity entity in source.ToList()) { var row = response.NewRow(); row.SetRowId(string.Empty + entity.GetHashCode()); foreach (var column in dataTableInit.Columns) { // if the column specifies a formatting function if (column.Format != null) { var method = column.Format.Compile(); row.PushColumn(method.Invoke(entity).ToString()); } else { var value = DataBinder.Eval(entity, column.ColumnName); row.PushColumn(value != null ? value.ToString() : ""); } } } return(new JsonResult { Data = response, ContentType = null, ContentEncoding = null, JsonRequestBehavior = JsonRequestBehavior.AllowGet }); }