/// <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&lt;object&gt;
        /// 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
            });
        }