public class UserProfile { [Key] public int UserId { get; set; } public string UserName { get; set; } public int? UserGroupId { get; set; } public virtual UserGroup UserGroup { get; set; } } public class UserGroup { [Key] public int UserGroupId { get; set; } [DisplayName("Group Name")] public string GroupName { get; set; } [DisplayName("Group Description")] public string Description { get; set; } public virtual ICollection<UserProfile> Users { get; set; } }
public ActionResult Index() { var userProfiles = _db.UserProfiles.Include(c => c.UserGroup); return View(userProfiles.ToList()); }
@Html.DisplayNameFor(model => model.UserGroup.GroupName)
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public sealed class LookupAttribute : Attribute { public Type Model { get; set; } public string NameField { get; set; } }
public class UserProfile { [Key] public int UserId { get; set; } public string UserName { get; set; } [Lookup(Model = typeof(UserGroup), NameField = "GroupName")] public int? UserGroupId { get; set; } public virtual UserGroup UserGroup { get; set; } }
public class LookupMetadataExtension : DataAnnotationsModelMetadataProvider { protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) { var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); var additionalValues = attributes.OfType<LookupAttribute>().FirstOrDefault(); if (additionalValues != null) { metadata.AdditionalValues.Add(LookupConsts.LookupMetadata, additionalValues); } return metadata; } }
ModelMetadataProviders.Current = new LookupMetadataExtension();
public static MvcHtmlString LookupFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string filterAction, Type modelType, String nameField, IDictionary<string, object> htmlAttributes) { var fieldName = ExpressionHelper.GetExpressionText(expression); var commonMetadata = PrepareLookupCommonMetadata( ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData), htmlHelper.ViewData.ModelMetadata, modelType, nameField); var lookupAttribute = commonMetadata.AdditionalValues[LookupConsts.LookupMetadata] as LookupAttribute; return LookupHtmlInternal(htmlHelper, commonMetadata, lookupAttribute, fieldName, filterAction, htmlAttributes); }
private static ModelMetadata PrepareLookupCommonMetadata(ModelMetadata fieldMetadata, ModelMetadata modelMetadata , Type modelType, String nameField) { LookupAttribute lookupMetadata; if (modelType != null && nameField != null) { lookupMetadata = new LookupAttribute { Model = modelType, NameField = nameField }; if (fieldMetadata.AdditionalValues.ContainsKey(LookupConsts.LookupMetadata)) fieldMetadata.AdditionalValues.Remove(LookupConsts.LookupMetadata); fieldMetadata.AdditionalValues.Add(LookupConsts.LookupMetadata, lookupMetadata); }
if (fieldMetadata.AdditionalValues != null && fieldMetadata.AdditionalValues.ContainsKey(LookupConsts.LookupMetadata)) { lookupMetadata = fieldMetadata.AdditionalValues[LookupConsts.LookupMetadata] as LookupAttribute; if (lookupMetadata != null) { var prop = lookupMetadata.Model.GetPropertyWithAttribute("KeyAttribute"); var releatedTableKey = prop != null ? prop.Name : String.Format("{0}Id", lookupMetadata.Model.Name); fieldMetadata.AdditionalValues.Add("idField", releatedTableKey); var releatedTableMetadata = modelMetadata.Properties.FirstOrDefault(proper => proper.PropertyName == lookupMetadata.Model.Name); if (releatedTableMetadata != null) { UpdateLookupColumnsInfo(releatedTableMetadata, fieldMetadata); UpdateNameFieldInfo(lookupMetadata.NameField, releatedTableMetadata, fieldMetadata); } else { throw new ModelValidationException(String.Format( "Couldn't find data from releated table. Lookup failed for model {0}", lookupMetadata.Model.Name)); } } } else { throw new ModelValidationException(String.Format("Couldn't find releated model type. Lookup field")); } return fieldMetadata; }
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class LookupGridColumnsAttribute : Attribute { public string[] LookupColumns { get; set; } public LookupGridColumnsAttribute(params string[] values) { LookupColumns = values; } }
[LookupGridColumns(new[] { "Description" })] public class UserGroup { [Key] public int UserGroupId { get; set; } [DisplayName("Group Name")] public string GroupName { get; set; } [DisplayName("Group Description")] public string Description { get; set; } public virtual ICollection<UserProfile> Users { get; set; } }
private static void UpdateLookupColumnsInfo(ModelMetadata releatedTableMetadata, ModelMetadata metadata) { IDictionary<string, string> columns = new Dictionary<string, string>(); var gridColumns = releatedTableMetadata.ModelType.GetCustomAttributeByType<LookupGridColumnsAttribute>(); if (gridColumns != null) { foreach (var column in gridColumns.LookupColumns) { var metadataField = releatedTableMetadata.Properties.FirstOrDefault( propt => propt.PropertyName == column); if (metadataField != null) { columns.Add(column, metadataField.DisplayName); } else { throw new ModelValidationException( String.Format("Couldn't find column in releated table {0}", releatedTableMetadata.GetDisplayName())); } } metadata.AdditionalValues.Add("lookupColumns", columns); } }
private static void UpdateNameFieldInfo(string nameField, ModelMetadata releatedTableMetadata, ModelMetadata commonMetadata) { var nameFieldMetedata = releatedTableMetadata.Properties.FirstOrDefault(propt => propt.PropertyName == nameField); if (nameFieldMetedata != null) { commonMetadata.AdditionalValues.Add("lookupFieldValue", nameFieldMetedata.SimpleDisplayText); commonMetadata.AdditionalValues.Add("lookupFieldDisplayValue", nameFieldMetedata.DisplayName); } else { throw new ModelValidationException(String.Format("Couldn't find name field in releated table {0}", releatedTableMetadata.GetDisplayName())); } }
private static MvcHtmlString LookupHtmlInternal(HtmlHelper htmlHelper, ModelMetadata metadata, LookupAttribute lookupMetadata, string name, string action, IDictionary<string, object> htmlAttributes) { if (string.IsNullOrEmpty(name)) { throw new ArgumentException("Error", "htmlHelper"); } var divBuilder = new TagBuilder("div"); divBuilder.MergeAttribute("id", String.Format("{0}_{1}", name, "div")); divBuilder.MergeAttribute("class", "form-wrapper cf"); divBuilder.MergeAttribute("type", lookupMetadata.Model.FullName); divBuilder.MergeAttribute("nameField", lookupMetadata.NameField); divBuilder.MergeAttribute("idField", metadata.AdditionalValues["idField"] as string); divBuilder.MergeAttribute("nameFieldDisplay", metadata.AdditionalValues["lookupFieldDisplayValue"] as string); divBuilder.MergeAttribute("action", action);
var columnsDivBuilder = new TagBuilder("div"); columnsDivBuilder.MergeAttribute("id", String.Format("{0}_{1}", name, "columns")); columnsDivBuilder.MergeAttribute("style", "display:none"); if (metadata.AdditionalValues.ContainsKey("lookupColumns")) { var columns = ((IDictionary<string, string>)metadata.AdditionalValues["lookupColumns"]); var columnString = String.Empty; foreach (var column in columns.Keys) { var columnDiv = new TagBuilder("div"); columnDiv.MergeAttribute("colName", column); columnDiv.MergeAttribute("displayName", columns[column]); columnString += columnDiv.ToString(TagRenderMode.SelfClosing); } columnsDivBuilder.InnerHtml = columnString; }
var inputBuilder = new TagBuilder("input"); inputBuilder.MergeAttributes(htmlAttributes); inputBuilder.MergeAttribute("type", "text"); inputBuilder.MergeAttribute("class", "lookup", true); inputBuilder.MergeAttribute("id", String.Format("{0}_{1}", name, "lookup"), true); inputBuilder.MergeAttribute("value", metadata.AdditionalValues["lookupFieldValue"] as string, true); var hiddenInputBuilder = new TagBuilder("input"); hiddenInputBuilder.MergeAttribute("type", "hidden"); hiddenInputBuilder.MergeAttribute("name", name, true); hiddenInputBuilder.MergeAttribute("id", name, true); hiddenInputBuilder.MergeAttribute("value", metadata.SimpleDisplayText, true); var buttonBuilder = new TagBuilder("input"); buttonBuilder.MergeAttribute("type", "button"); buttonBuilder.MergeAttribute("value", "Lookup"); buttonBuilder.MergeAttribute("class", "lookupbutton"); buttonBuilder.MergeAttribute("id", String.Format("{0}_{1}", name, "lookupbtn"), true);
divBuilder.InnerHtml = String.Format(@"{0}{1}{2}{3}", inputBuilder.ToString(TagRenderMode.SelfClosing), hiddenInputBuilder.ToString(TagRenderMode.SelfClosing), buttonBuilder.ToString(TagRenderMode.SelfClosing), columnsDivBuilder.ToString(TagRenderMode.Normal) ); return new MvcHtmlString(divBuilder.ToString(TagRenderMode.Normal)); }
public static MvcHtmlString LookupFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) { var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext); return LookupFor(htmlHelper, expression, urlHelper.Action("LookupData"), null, null, null); } public static MvcHtmlString LookupFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string filterAction) { return LookupFor(htmlHelper, expression, filterAction, null, null, null); }
public static IQueryable<T> Where<T>(this IQueryable<T> source, string fieldName, string searchString, string compareFunction) { if (searchString == null) searchString = String.Empty; var param = Expression.Parameter(typeof(T)); var prop = Expression.Property(param, fieldName); var methodcall = Expression.Call(prop, typeof(String).GetMethod(compareFunction, new[] { typeof(string) }), Expression.Constant(value: searchString)); var lambda = Expression.Lambda<Func<T, bool>>(methodcall, param); var request = source.Where(lambda); return request; }
public static IQueryable<T> WhereStartsWith<T>(this IQueryable<T> source, string fieldName, string searchString) { return Where(source, fieldName, searchString, "StartsWith"); } public static IQueryable<T> WhereContains<T>(this IQueryable<T> source, string fieldName, string searchString) { return Where(source, fieldName, searchString, "Contains"); }
public static IQueryable<T> Equal<T>(this IQueryable<T> source, string fieldName, string searchString) { if (searchString == null) searchString = String.Empty; var param = Expression.Parameter(typeof(T)); var prop = Expression.Property(param, fieldName); var methodcall = Expression.Equal(prop, Expression.Constant(searchString)); var lambda = Expression.Lambda<Func<T, bool>>(methodcall, param); var request = source.Where(lambda); return request; } public static IQueryable<T> NotEqual<T>(this IQueryable<T> source, string fieldName, string searchString) { if (searchString == null) searchString = String.Empty; var param = Expression.Parameter(typeof(T)); var prop = Expression.Property(param, fieldName); var methodcall = Expression.NotEqual(prop, Expression.Constant(searchString)); var lambda = Expression.Lambda<Func<T, bool>>(methodcall, param); var request = source.Where(lambda); return request; }
static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName) { var type = typeof(T); var param = Expression.Parameter(type); var pr = type.GetProperty(prop); var expr = Expression.Property(param, type.GetProperty(prop)); var ptype = pr.PropertyType; var delegateType = typeof(Func<,>).MakeGenericType(type, ptype); var lambda = Expression.Lambda(delegateType, expr, param); var result = typeof(Queryable).GetMethods().Single( method => method.Name == methodName && method.IsGenericMethodDefinition && method.GetGenericArguments().Length == 2 && method.GetParameters().Length == 2) .MakeGenericMethod(type, ptype) .Invoke(null, new object[] { source, lambda }); return (IOrderedQueryable<T>)result; }
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, bool desc , string property) { return ApplyOrder(source, property, desc ? "OrderByDescending" : "OrderBy"); } public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property) { return ApplyOrder(source, property, "OrderBy"); } public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property) { return ApplyOrder(source, property, "OrderByDescending"); } public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property) { return ApplyOrder(source, property, "ThenBy"); } public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property) { return ApplyOrder(source, property, "ThenByDescending"); }
public enum SearchOperator { Equal, NotEqual, Contains } public class FilterSettings { public string SearchString; public string SearchField; public SearchOperator Operator; } public class GridSettings { public bool IsSearch { get; set; } public int PageSize { get; set; } public int PageIndex { get; set; } public string SortColumn { get; set; } public bool Asc { get; set; } } public class LookupSettings { public Type Model { get; set; } public FilterSettings Filter { get; set; } public GridSettings GridSettings { get; set; } public string IdField { get; set; } public string NameField { get; set; } }
public class LookupModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { HttpRequestBase request = controllerContext.HttpContext.Request; var lookupSettings = new LookupSettings { Model = Type.GetType(request["modelType"]), IdField = request["IdField"], NameField = request["NameField"], Filter = new FilterSettings { SearchString = request["searchString"] ?? String.Empty, SearchField = request["searchField"] } }; if(request["searchOper"] != null) { switch (request["searchOper"]) { case "eq": lookupSettings.Filter.Operator = SearchOperator.Equal; break; case "ne": lookupSettings.Filter.Operator = SearchOperator.NotEqual; break; case "cn": lookupSettings.Filter.Operator = SearchOperator.Contains; break; } } lookupSettings.GridSettings = new GridSettings {Asc = request["sord"] == "asc"}; if (request["_search"] != null) lookupSettings.GridSettings.IsSearch = Convert.ToBoolean(request["_search"]); if (request["page"] != null) lookupSettings.GridSettings.PageIndex = Convert.ToInt32(request["page"]); if (request["rows"] != null) lookupSettings.GridSettings.PageSize = Convert.ToInt32(request["rows"]); lookupSettings.GridSettings.SortColumn = request["sidx"]; if (lookupSettings.Filter.SearchField == null) { lookupSettings.Filter.SearchField = request["NameField"]; lookupSettings.Filter.Operator = SearchOperator.Contains; } return lookupSettings; } }
ModelBinders.Binders.Add(typeof(LookupSettings), new LookupModelBinder());
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); ModelMetadataProviders.Current = new LookupMetadataExtension(); ModelBinders.Binders.Add(typeof(LookupSettings), new LookupModelBinder()); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); AuthConfig.RegisterAuth(); }
public virtual ActionResult LookupData([ModelBinder(typeof(LookupModelBinder))] LookupSettings settings)
private static ActionResult LookupMethodCall(string methodName, LookupSettings settings, DbContext dbContext, OnAfterQueryPrepared onAfterQueryPrepared) { var methodLookupCall = typeof(LookupDataResolver). GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static); methodLookupCall = methodLookupCall.MakeGenericMethod(settings.Model); var lookupSettings = Expression.Parameter(typeof(LookupSettings), "settings"); var dbCtx = Expression.Parameter(typeof(DbContext), "dbContext"); var funct = Expression.Parameter(typeof(OnAfterQueryPrepared), "onAfterQueryPrepared"); var lookupSearch = Expression.Lambda( Expression.Call( null, methodLookupCall, lookupSettings, dbCtx, funct), lookupSettings, dbCtx, funct); var lookupSearchDelegate = (Func<LookupSettings, DbContext, OnAfterQueryPrepared, JsonResult>) lookupSearch.Compile(); return lookupSearchDelegate(settings, dbContext, onAfterQueryPrepared); }
public static ActionResult BasicLookup(LookupSettings settings, DbContext dbContext, OnAfterQueryPrepared onAfterQueryPrepared) { return LookupMethodCall("LookupSearch", settings, dbContext, onAfterQueryPrepared); } public static ActionResult BasicGrid(LookupSettings settings, DbContext dbContext, OnAfterQueryPrepared onAfterQueryPrepared) { return LookupMethodCall("LookupDataForGrid", settings, dbContext, onAfterQueryPrepared); }
private static JsonResult LookupSearch<T>(LookupSettings settings, DbContext dbContext, OnAfterQueryPrepared onAfterQueryPrepared) where T : class { var modelType = typeof(T); var request = dbContext.Set<T>().AsQueryable(); if (onAfterQueryPrepared != null) { var query = onAfterQueryPrepared(request, settings); if (query != null) request = query.Cast<T>(); } request = request.WhereStartsWith(settings.Filter.SearchField, settings.Filter.SearchString); return new JsonResult { Data = request.ToList().Select(t => new { label = modelType.GetProperty(settings.NameField).GetValue(t).ToString(), id = modelType.GetProperty(settings.IdField).GetValue(t).ToString() }).ToList(), ContentType = null, ContentEncoding = null, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; }
private static JsonResult LookupDataForGrid<T>(LookupSettings settings, DbContext dbContext, OnAfterQueryPrepared onAfterQueryPrepared) where T : class { var modelType = typeof(T); var pageIndex = settings.GridSettings.PageIndex - 1; var pageSize = settings.GridSettings.PageSize; var request = dbContext.Set<T>().AsQueryable(); if (onAfterQueryPrepared != null) { var query = onAfterQueryPrepared(request, settings); if (query != null) request = query.Cast<T>(); } if (settings.GridSettings.IsSearch) { switch (settings.Filter.Operator) { case SearchOperator.Equal: request = request.Equal(settings.Filter.SearchField, settings.Filter.SearchString); break; case SearchOperator.NotEqual: request = request.NotEqual(settings.Filter.SearchField, settings.Filter.SearchString); break; case SearchOperator.Contains: request = request.WhereContains(settings.Filter.SearchField, settings.Filter.SearchString); break; } } var totalRecords = request.Count(); var totalPages = (int)Math.Ceiling(totalRecords / (float)pageSize); var userGroups = request .OrderBy(!settings.GridSettings.Asc, settings.GridSettings.SortColumn) .Skip(pageIndex * pageSize) .Take(pageSize); return new JsonResult { Data = new { total = totalPages, settings.GridSettings.PageIndex, records = totalRecords, rows = ( userGroups.AsEnumerable().Select(t => new { id = modelType.GetProperty(settings.IdField).GetValue(t).ToString(), cell = GetDataFromColumns(modelType, settings, t) }).ToList()) }, ContentType = null, ContentEncoding = null, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; }
private static IEnumerable<string> GetDataFromColumns(Type model, LookupSettings settings, object instance) { var dataArray = new List<string> { model.GetProperty(settings.IdField).GetValue(instance).ToString(), model.GetProperty(settings.NameField).GetValue(instance).ToString() }; var gridColumns = model.GetCustomAttributeByType<LookupGridColumnsAttribute>(); if (gridColumns != null) { dataArray.AddRange(from column in gridColumns.LookupColumns select model.GetProperty(column).GetValue(instance) into val where val != null select val.ToString()); } return dataArray; }
public class LookupBasicController : Controller { protected virtual DbContext GetDbContext { get { throw new NotImplementedException("You have to implement this method to return correct db context"); } } protected virtual IQueryable LookupBaseQuery(IQueryable query, LookupSettings settings) { return null; } public virtual ActionResult LookupData([ModelBinder(typeof(LookupModelBinder))] LookupSettings settings) { return LookupDataResolver.BasicLookup(settings, GetDbContext, LookupBaseQuery); } public virtual ActionResult LookupDataGrid([ModelBinder(typeof(LookupModelBinder))] LookupSettings settings) { return LookupDataResolver.BasicGrid(settings, GetDbContext, LookupBaseQuery); }
[Table("UserProfile")] public class UserProfile { [Key] [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)] public int UserId { get; set; } public string UserName { get; set; } [Lookup(Model = typeof(UserGroup), NameField = "GroupName")] public int? UserGroupId { get; set; } public virtual UserGroup UserGroup { get; set; } } [LookupGridColumns(new[] { "Description" })] public class UserGroup { [Key] public int UserGroupId { get; set; } [DisplayName("Group Name")] public string GroupName { get; set; } [DisplayName("Group Description")] public string Description { get; set; } public virtual ICollection<UserProfile> Users { get; set; } }
public class UserListController : LookupBasicController { private readonly DataBaseContext _db = new DataBaseContext(); protected override DbContext GetDbContext { get { return _db; } }
public ActionResult Edit(int id = 0) { UserProfile userprofile = _db.UserProfiles.Include(c => c.UserGroup) .SingleOrDefault(x => x.UserId == id); if (userprofile == null) { return HttpNotFound(); } return View(userprofile); }
@using TestApp.Models @model UserProfile @{ ViewBag.Title = "Edit"; } @Styles.Render("~/Content/JqGrid") <h2>Edit</h2> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>UserProfile</legend> @Html.HiddenFor(model => model.UserId) <div class="editor-label"> @Html.LabelFor(model => model.UserName) </div> <div class="editor-field"> @Html.EditorFor(model => model.UserName) @Html.ValidationMessageFor(model => model.UserName) </div> <div class="editor-label"> @Html.LabelFor(model => model.UserGroupId) </div> <div class="editor-field"> @Html.LookupFor(model => model.UserGroupId) @Html.ValidationMessageFor(model => model.UserGroupId ) </div> <p> <input type="submit" value="Save" /> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/lookup") @Scripts.Render("~/bundles/jqueryval") @Scripts.Render("~/bundles/jqueryui") @Scripts.Render("~/bundles/jqgrid") }
Source: https://habr.com/ru/post/190482/
All Articles