the class has a property UserId which type of ApplicationUser and its required, and of course we can not pass it from view to controller because of security reasons.
Now when the controller checks the state of model the model is not in a correct state because there is no UserId Value and it returns the view back, if I use the view model the class has more than 50 properties and assigning the values from view model to class and then save it it is very tedious and difficult to do it for saving editing and so on, any advice to come out from this problem
thanks
You can use Action Filters attribute to Auto Bind Property.
Let's say we have UserSettingMetaModel with UserId property.
public interface IAutoBindingUserId
{
int UserId { get; set; }
}
public class UserSettingMetaModel : IAutoBindingUserId
{
public int Id { get; set; }
[Required]
public int UserId { get; set; }
// The rest of properties
}
In UserSetting controller, we have
[HttpPost]
[AutoBindProperty]
public JsonResult Add(UserSettingMetaModel metaModel)
{
if (ModelState.IsValid)
{
// do something
}
}
Behind the sense of AutoBindProperty Attribute
[AttributeUsage(AttributeTargets.Method)]
public sealed class AutoBindPropertyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var actionParams = filterContext.ActionParameters.Values.FirstOrDefault();
Bind<IAutoBindingUserId>(model => model.UserId = 123, actionParams);
// Assuming that you know the way get userId value here.
}
private static void Bind<T>(Action<T> doBinding, object actionParams) where T : class
{
if (actionParams is T model)
{
doBinding(model);
}
}
}
anyhow thanks for the cooperation for solving this problem
the easiest way that i found to return a UserId from a class, just one line of code is required inside class constructor
public constructor()
{
UserId = ClaimsPrincipal.Current.Identity.GetUserId();
}
Related
I have an action that will be called with optional querystring parameters. These parameters however are contained in different view models. When I try and add these models to my list of parameters, only a single one is filled and the others are always null. With the exception of an empty query string, where all models are instantiated with defaults.
It is not an option to nest these models for the reason that I don't want the nested property name to be visible in the querystring. So unless that can be circumvented somehow, that would also be a viable solution.
I noticed that, when creating a quick override of the DefaultModelBuilder, all models are parsed but the end result is still that only one model is actually assigned.
This is my scenario:
public ActionResult Index(ModelA ma, ModelB ba)
{
return Content("ok");
}
public class ModelA
{
public string Test { get; set; }
public string Name { get; set; }
}
public class ModelB
{
public int? SomeInteger { get; set; }
public int? TestInteger { get; set; }
}
Desired querystring:
index?Test=Hi&SomeInteger=7
What I want to avoid:
index?ModelA.Test=Hi&ModelB.SomeInteger=7
You can try to make a class combining those two:
public class ModelPair
{
public ModelA A { get; set; }
public ModelB B { get; set; }
}
And then with
public ActionResult Index(ModelPair mp)
{
return Content("ok");
}
You can do ?A.Test=blah&B.SomeInteger=42
I ended up delving in to creating my own custom model binder that does recursive binding. So long as property names are not re-used, which is not a thing that will happen in my models anyway, this fixes my issue of not exposing the property names of the nested model classes.
So now I have the following class structure:
public class ModelA
{
public string Test { get; set; }
public string Name { get; set; }
}
public class ModelB
{
public int? SomeInteger { get; set; }
public int? TestInteger { get; set; }
}
public class ViewModel
{
public ModelA ModelA { get; set; }
public ModelB ModelB { get; set; }
}
And the action now looks like this
public ActionResult Index(ViewModel model)
{
return Content("ok");
}
Which will let me use the following querystring without exposing the ugly property names:
index?Test=Hi&SomeInteger=7&Name=Yep&TestInteger=72
Of course I haven't tested this for an extensive period yet so I have no idea what problems lurk around the corner, but all of the nested models are now properly filled with the data from the querystring and the model classes can be easily re-used : )
public class RecursiveModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = base.BindModel(controllerContext, bindingContext);
if (model != null)
{
var properties = bindingContext.ModelType.GetProperties().Where(x => x.PropertyType.IsClass && !x.PropertyType.Equals(typeof(string)) );
foreach(var property in properties)
{
var resursiveBindingContext = new ModelBindingContext(bindingContext)
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, property.PropertyType)
};
var recursiveModel = BindModel(controllerContext, resursiveBindingContext);
property.SetValue(model, recursiveModel);
}
}
return model;
}
}
As far as I know, the Default Model Binder cannot do that. we have to implement custom Model Binder as follow.
public class CustomModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var query = bindingContext.HttpContext.Request.Query;
var modelb = new ModelB();
if (query.TryGetValue($"{bindingContext.ModelName}.{nameof(modelb.SomeInteger)}", out var someInteger))
{
modelb.SomeInteger = Convert.ToInt32(JsonConvert.DeserializeObject(someInteger).ToString());
}
if (query.TryGetValue($"{bindingContext.ModelName}.{nameof(modelb.TestInteger)}", out var testInteger))
{
modelb.TestInteger = Convert.ToInt32(JsonConvert.DeserializeObject(testInteger).ToString());
}
bindingContext.Result = ModelBindingResult.Success(modelb);
return Task.FromResult(modelb);
}
}
In the controller Action we can use Binder as follow
public IActionResult Index(ModelA modelA, [ModelBinder(typeof(CustomModelBinder))]ModelB modelB)
{
return Json(new {modelA, modelB});
}
And in querystring we can have prefix to differentiate each model.
?modelA.test="MATests"&modelA.Name="modelANameValue"&modelB.SomeInteger="5"
Please find working sample here on github
I am building a simple search, sort, page feature. I have attached the code below.
Below are the usecases:
My goal is to pass the "current filters" via each request to persist them particularly while sorting and paging.
Instead of polluting my action method with many (if not too many) parameters, I am thinking to use a generic type parameter that holds the current filters.
I need a custom model binder that can be able to achieve this.
Could someone please post an example implementation?
PS: I am also exploring alternatives as opposed to passing back and forth the complex objects. But i would need to take this route as a last resort and i could not find a good example of custom model binding generic type parameters. Any pointers to such examples can also help. Thanks!.
public async Task<IActionResult> Index(SearchSortPage<ProductSearchParamsVm> currentFilters, string sortField, int? page)
{
var currentSort = currentFilters.Sort;
// pass the current sort and sortField to determine the new sort & direction
currentFilters.Sort = SortUtility.DetermineSortAndDirection(sortField, currentSort);
currentFilters.Page = page ?? 1;
ViewData["CurrentFilters"] = currentFilters;
var bm = await ProductsProcessor.GetPaginatedAsync(currentFilters);
var vm = AutoMapper.Map<PaginatedResult<ProductBm>, PaginatedResult<ProductVm>>(bm);
return View(vm);
}
public class SearchSortPage<T> where T : class
{
public T Search { get; set; }
public Sort Sort { get; set; }
public Nullable<int> Page { get; set; }
}
public class Sort
{
public string Field { get; set; }
public string Direction { get; set; }
}
public class ProductSearchParamsVm
{
public string ProductTitle { get; set; }
public string ProductCategory { get; set; }
public Nullable<DateTime> DateSent { get; set; }
}
First create the Model Binder which should be implementing the interface IModelBinder
SearchSortPageModelBinder.cs
public class SearchSortPageModelBinder<T> : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
SearchSortPage<T> ssp = new SearchSortPage<T>();
//TODO: Setup the SearchSortPage<T> model
bindingContext.Result = ModelBindingResult.Success(ssp);
return TaskCache.CompletedTask;
}
}
And then create the Model Binder Provider which should be implementing the interface IModelBinderProvider
SearchSortPageModelBinderProvider.cs
public class SearchSortPageModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType.GetTypeInfo().IsGenericType &&
context.Metadata.ModelType.GetGenericTypeDefinition() == typeof(SearchSortPage<>))
{
Type[] types = context.Metadata.ModelType.GetGenericArguments();
Type o = typeof(SearchSortPageModelBinder<>).MakeGenericType(types);
return (IModelBinder)Activator.CreateInstance(o);
}
return null;
}
}
And the last thing is register the Model Binder Provider, it should be done in your Startup.cs
public void ConfigureServices(IServiceCollection services)
{
.
.
services.AddMvc(options=>
{
options.ModelBinderProviders.Insert(0, new SearchSortPageModelBinderProvider());
});
.
.
}
I'm working on a basic MVC5/EF6 application and am running into the following error:
No parameterless constructor defined for this object.
This happens when I use the default Create Action and View that are scaffolded by Visual Studio 2013 when you create a new Controller. I have not adjusted anything within those generated files (TestItemController, Views/TestItem/Create.cshtml). My entities on which the controller is scaffolded look like this:
public class TestItem
{
private Category _category;
// Primary key
public int TestItemId { get; set; }
public int CategoryId { get; set; }
public string TestColumn { get; set; }
public virtual Category Category {
get { return _category; }
set { _category = value; }
}
protected TestItem()
{
}
public TestItem(Category category)
{
_category = category;
}
}
public class Category
{
private ICollection<TestItem> _testItems;
// Primary key
public int CategoryId { get; set; }
public string Description { get; set; }
public virtual ICollection<TestItem> TestItems
{
get { return _faqs; }
set { _faqs = value; }
}
public Category()
{
_testItems = new List<TestItem>();
}
}
I'm guessing this is due to the TestItem class having the constructor taking in a Category object, which is there to keep the domain model anemic. A TestItem cannot be created without a Category. But as far as I know the protected parameterless constructor should be used by EF in this exact case when lazy loading etc.
What's going on here, or what am I doing wrong?
UPDATE:
The controller looks like this (trimmed):
public class TestItemsController : Controller
{
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "TestItemId,OtherColumns")] TestItem testItem)
{
if (ModelState.IsValid)
{
db.TestItems.Add(testItem);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(testItem);
}
}
Sure, EF can use protected constructors, but scaffolding creates action methods for creating a new item. These action methods require a parameterless public constructor.
You can find some details of these create methods here.
I'm starting to working on ASP.NET using MVC. I writing to action results, one of them is a HTTP GET and the another HTTP POST
[HttpGet]
public ActionResult DoTest()
{
Worksheet worksheets = new worksheets(..);
return View(w);
}
[HttpPost]
public ActionResult DoTest(Worksheet worksheet)
{
return PartialView("_Problems", worksheet);
}
Now, Worksheet class has a property called Problems and this is a collection, but uses as an abstract class item.
public class Worksheet
{
public List<Problem> Problems { get; set; }
}
Here's my abstract class and one implementation
public abstract class Problem
{
public Problem() { }
public int Id { get; set; }
public abstract bool IsComplete { get; }
protected abstract bool CheckResponse();
}
public class Problem1 : Problem
{
...
public decimal CorrectResult { get; set; }
// this is the only property of my implementation class which I need
public decimal? Result { get; set;}
public override bool IsComplete
{
get { return Result.HasValue; }
}
protected override bool CheckResponse()
{
return this.CorrectResult.Equals(this.Result.Value);
}
}
I have right now, many implementations of Problem class, but I really need to get just one value of my implementation class. But it thrown the above image error.
What can I do to allow model binder recover that part of my abstracts classes
The following code would not compile:
var problem = new Problem();
... because the Problem class is abstract. The MVC engine cannot just create a Problem directly. Unless you give it some way to know which type of Problem to instantiate, there's nothing it can do.
It is possible to create your own ModelBinder implementation, and tell MVC to use it. Your implementation could be tied to a Dependency Injection framework, for example, so that it knows to create a Problem1 whenever a Problem class is requested.
Or you could simply change your action method to take a concrete type:
public ActionResult DoTest(IEnumerable<Problem1> problems)
{
return PartialView("_Problems",
new Worksheet {
Problems = problems.Cast<Problem>().ToList()
});
}
Let's say you have an object called Person that looks like this:
class Person
{
public int ID { get; set; }
public string Name { get; set; }
public int NumberOfCatsNamedEnder { get; set; }
}
I have a simple HTML form that exposes the properties that gets posted to an ASP.NET MVC action inside of my PersonController class. The issue I have is that if someone puts in the letter 'A' for NumberOfCatsNamedEnder, I get a The model of type 'Person' was not successfully updated. error. Since this happens while trying to update the Model, I can't find any way to check to see if someone passed in a non-integer value without resorting to
if(!IsInteger(formCollection["NumberOfCatsNamedEnder"]))
{
ModelState.AddModelError(
"NumberOfCatsNamedEnder",
"Ender count should be a number");
}
Is there a better way to do this? I was able to find some information on custom ModelBinders; is that what is needed?
I really like the approach of using a presentation model. I'd create a class like this:
class PersonPresentation
{
public int ID { get; set; }
public string Name { get; set; }
public string NumberOfCatsNamedEnder { get; set; }
public void FromPerson(Person person){ /*Load data from person*/ }
}
Then your controller action can bind the view to a PersonPresentation:
public ActionResult Index()
{
Person person = GetPerson();
PersonPresentation presentation = new PersonPresentation();
ViewData.Model = presentation.FromPerson(person);
return View();
}
...and then accept one in your Update method and perform validation:
public ActionResult Update(PersonPresentation presentation)
{
if(!IsInteger(presentation.NumberOfCatsNamedEnder))
{
ModelState.AddModelError(
"NumberOfCatsNamedEnder",
"Ender count should be a number");
}
...
}