I do not want do bind the Id property on my CustomerViewModel so I added a [BindNever] attribute but it is not working. What could be the solution?
I have the following:
CustomerController.cs
// PUT api/customers/5
[HttpPut("{id}")]
public async Task<IActionResult> Put([FromUri] int id, [FromBody]CustomerViewModel customer)
{
//Implementation
}
CustomerViewModel
public class CustomerViewModel
{
[BindNever]
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Email { get; set; }
}
If I input the following json . The id property still gets binded
{
"id": 100,
"lastName": "Bruce",
"firstName": "Wayne",
"email": "bruce#gothamcity.com"
}
This Blog post is an interesting read and concludes that the [FromBody] annotation "overrides" the BindBehaviourAttribute (BindNever is a simple specialization). The model is populated by all data available from the body (your JSON data in this case).
I do not consider this as intuitive, and the issue has a nice statement about this:
[BindRequired] customizes the MVC model binding system . That's its
purpose and it's working as designed.
[FromBody] switches the affected property or parameter into the
different world of input formatting. Each input formatter (e.g.
Json.NET and a small MVC-specific wrapper) can be considered a
separate system with its own customization. The model binding system
has no knowledge the details of JSON (or any other) deserialization.
Lesson learned: BindNever does not work in this scenario.
What are alternatives ?
Solution 1: Writing some custom model binding code. I have not done it myself, but What is the correct way to create custom model binders in MVC6? may help.
Solution 2: Rather pragmatic one
Perhaps this simple (but not very nice) workaround helps you out:
[HttpPut("{id}")]
public async Task<IActionResult> Put([FromUri] int id, [FromBody]CustomerViewModel customer)
{
customer.Id = 0;
//Implementation
}
also you could do this
public class CustomerViewModel
{
public int Id { get; private set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Email { get; set; }
}
I add a note.
Now it's officially explained by Microsoft.
https://learn.microsoft.com/ja-jp/aspnet/core/mvc/models/model-binding?view=aspnetcore-6.0#attributes-for-complex-type-targets
https://learn.microsoft.com/ja-jp/aspnet/core/mvc/models/model-binding?view=aspnetcore-6.0#input-formatters
https://learn.microsoft.com/ja-jp/aspnet/core/mvc/models/model-binding?view=aspnetcore-6.0#frombody-attribute
In summary,
If we use the “FromBody attribute (including defaults such as HttpPost attribute)”, it depends on the input formatter and the BindNever attribute etc. will not work.
Instead, we can do so by specifying the attribute that corresponds to the input formatter.
For example, for the default json
It can be ignored using "System.Text.Json.Serialization.JsonIgnoreAttribute".
Try NotMapped attribute.
Body must be at least 30 characters; you entered 24.
Related
Is there any data annotation for the allowed values in ASP.NET MVC Core? Since there is no enum in SQL server I am not able to migrate my class with enum field in it to the database. I want to give possible/allowed values to the field in the class. Is there any way to do this?
public class Employee
{
[Key]
public int ID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Status { get; set; }
}
I want to provide Active and Inactive as the only possible values to the Status field.
you can also do this using a regular expression as below:
[Required]
[RegularExpression("Active|Inactive", ErrorMessage = "Invalid Status")]
public string Status { get; set; }
More details can by found here
As #ps2goat mentioned, you could use a check constraint on your database. However, for the model coming into the API you probably still want to provide validation there. Ideally you will do what you can, within reason, to prevent bad data from ever getting to the data layer. You don't mention whether you're using an n-tier architecture, or if your controller is directly referencing the data model. Either way, I believe this custom attribute can be used either at the API layer or on the entity model.
This is a good answer that explains how to create a custom validation attribute. It's an old answer, but it still applies to .Net Core. And here is an answer for a custom validation attribute in .Net Core. It basically looks like this:
public class EmployeeStatusAttribute : ValidationAttribute
{
private string[] _allowedValues;
public EmployeeStatusAttribute(string[] allowedValues)
{
_allowedValues = allowedValues;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var employee = value as Employee;
if (_allowedValues.Contains(employee.Status))
{
return ValidationResult.Success;
}
return new ValidationResult(`{employee.Status} is not a valid status`);
}
}
Then in your model:
public class Employee
{
...
[EmployeeStatus("Active", "Inactive")]
public string Status { get; set; }
...
}
In my ASP.NET Core 2.1 MVC application, I want to expose a route like this:
/address/v1/postcode/{postcode}/street/{street}
I have defined my controller like this:
[Route("address/v1")]
[ApiController]
public class StreetController : ControllerBase
{
[HttpGet("postcode/{postcode}/street/{street}")]
public ActionResult<GetStreetDetailsResponse> GetStreetDetails([FromRoute] GetStreetDetailsRequest request)
{
throw new NotImplementedException();
}
}
public class GetStreetDetailsRequest
{
[Required]
[StringLength(4, MinimumLength = 4)]
[RegularExpression("^[\\d]+$")]
public string Postcode { get; set; }
[Required]
public string Street { get; set; }
}
public class GetStreetDetailsResponse
{
}
The route resolves as expected, however, the framework is not deserializing the Postcode and Street values, and those properties are not populated correctly in GetStreetDetailsRequest.
For example, if I call:
http://localhost/address/v1/postcode/0629/street/whatever
when it gets into the action method, the value of request.Postcode="{postcode}" and request.Street="{street}".
The issue appears to be due to the casing of my property names, because if I change GetStreetDetailsRequest to:
public class GetStreetDetailsRequest
{
[Required]
[StringLength(4, MinimumLength = 4)]
[RegularExpression("^[\\d]+$")]
public string postcode { get; set; }
[Required]
public string street { get; set; }
}
everything works correctly. However, I’m not happy with that solution as it doesn't follow conventional C# naming standards.
I’ve tried decorating the properties with [DataMember(Name="postcode")] or [JsonProperty("postcode")], but these seem to be ignored too.
For the record, in my Startup.ConfigureServices() method I am using the default serializer, which I understand supports camel case:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
Has anyone got a solution that will enable me to expose the route with camel case properties using Pascal case in the request object property names?
Well, you are correct in some way. This:
[HttpGet("postcode/{postcode}/street/{street}")]
Says you have a postcode and a street property, and you have none of them. If you want the default binding to work, the casing must match exactly:
[HttpGet("postcode/{Postcode}/street/{Street}")]
I am very new in dynamodb. I am following http://www.rkconsulting.com/blog/persistence-model-framework-with-aws-dynamodb
step by step tutorial for connecting and CRUD operation in dynamodb and it`s works fine.
In that tutorial they using attribute mapping for map class properties
[DynamoDBTable("Dinosaur")]
public class Dinosaur
{
[DynamoDBHashKey]
public string Id { get; set; }
[DynamoDBProperty(AttributeName = "Name")]
public string Name { get; set; }
[DynamoDBProperty(AttributeName = "HeightMetres")]
public double HeightMetres { get; set; }
[DynamoDBProperty(AttributeName = "WeightKG")]
public double WeightKg { get; set; }
[DynamoDBProperty(AttributeName = "Age")]
public int Age { get; set; }
[DynamoDBProperty(AttributeName = "Characteristics")]
public List<string> Characteristics { get; set; }
[DynamoDBProperty(AttributeName = "Photo", Converter = typeof(ImageConverter))]
public Image Photo { get; set; }
[DynamoDBIgnore]
public int IgnoreMe { get; set; }
}
My question is there any way to map class properties without using attribute ?
like as mongoDb
public class Employee
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
}
we can write this in this way in a separate class
BsonClassMap.RegisterClassMap<Employee>(cm => {
cm.AutoMap();
cm.IdMemberMap.SetRepresentation(BsonType.ObjectId);
});
Is it possible in dynamodb ?
In the latest version of the .NET SDK you don't have to put in the attribute tags, it will see all read/write properties and upload the attributes as the same name. You would only have to use the [DynamoDBProperty(...)] if you want the attribute name in DynamoDB to be something other than the .NET object name.
So in your case you could simply remove that attribute for all properties except photo (which needs the converter, you could remove the AttributeName part of it) and WeightKg (because the capitalization is different) and you would get the same result.
I see this is a little bit older question now, so it may not have been that way in older versions (not sure) but I'm using 3.3.0.0 of the SDK and it does work that way. You have probably moved on but answering for others that may come upon this thread as I did...
There is no way, the default "strongly typed" client relies on attributes.
If you have time to do the plumbing yourself - there is nothing stopping your from doing your own implementation of the POC to Dynamo mapping though. Amazon client api (AWSSDK.DynamoDBv2) exposes the raw class AmazonDynamoDBClient which handles all the API calls and the DynamoDBConext is just implementation of IDynamoDBContext interface - which exposes all the "strongly typed" operations. So you can make your own implementation and take different mapping approach in it.
Also you can make a feature request for this:
https://github.com/aws/aws-sdk-net/issues
What are good strategies for rebuilding/enriching a nested or complex ViewModel?
A common way to rebuild a flat ViewModel is shown here
But building and rebuilding a nested ViewModel using that method is too complex.
Models
public class PersonInfo
{
public int Id { get; set; }
public string Name { get; set; }
public int Nationality { get; set; }
public List<Address> Addresses { get; set; }
}
public class Address
{
public int AddressTypeID { get; set; }
public string Country { get; set; }
public string PostalCode { get; set; }
}
public class AddressType
{
public int Id { get; set; }
public string Description { get; set; }
}
view models
public class PersonEditModel
{
public int Id { get; set; }
public string Name { get; set; } //read-only
public int Nationality { get; set; }
public List<AddressEditModel> Addresses { get; set; }
public List<SelectListItem> NationalitySelectList { get; set; } //read-only
}
public class AddressEditModel
{
public int AddressTypeId { get; set; }
public string AddressDescription { get; set; } //read-only
public string Country { get; set; }
public string PostalCode { get; set; }
public List<SelectListItem> CountrySelectList { get; set; } //read-only
}
actions
public ActionResult Update(int id)
{
var addressTypes = service.GetAddressTypes();
var person = service.GetPerson(id);
var personEditModel= Map<PersonEditModel>.From(person);
foreach(var addressType in addressTypes)
{
var address = person.Addresses.SingleOrDefault(i => i.AddressTypeId == addressType.Id)
if(address == null)
{
personEditModel.Addresses.Add(new AddressEditModel
{
AddressTypeId = addressType.Id
});
}
else
{
personEditModel.Addresses.Add(Map<AddressEditModel>.From(address));
}
}
EnrichViewModel(personEditModel, person, addressTypes); //populate read-only data such as SelectList
return Index(personEditModel);
}
[HttpPost]
public ActionResult Update(PersonEditModel editModel)
{
if(!ModelState.IsValid)
{
var person = service.GetPerson(editModel.Id);
var addressTypes = service.GetAddressTypes();
EnrichViewModel(editModel, person, addressTypes);
return View(editModel);
}
service.Save(...);
return RedirectToAction("Index");
}
//populate read-only data such as SelectList
private void EnrichViewModel(PersonEditModel personEditModel, Person person, IEnumerable<AddressType> addressTypes)
{
personEditModel.Name = person.Name;
personEditModel.NationalitySelectList = GetNationalitySelectList();
foreach(var addressEditModel in personEditModel.Addresses)
{
addressEditModel.Description = addressTypes.Where(i => i.Id = addressEditModel.AddressTypeId).Select(i => i.Description).FirstOrDefault();
addressEditModel.CountrySelectListItems = GetCountrySelectList(addressEditModel.AddressTypeId);
}
}
My code for building and rebuilding the ViewModels (PersonEditModel and AddressEditModel) is too ugly. How do I restructure my code to clean this mess?
One easy way is to always build a new view model instead of merging/rebuilding since MVC will overwrite the fields with the values in ModelState anyway
[HttpPost]
public ActionResult Update(PersonEditModel editModel)
{
if(!ModelState.IsValid)
{
var newEditModel = BuildPersonEditModel(editModel.Id);
return View(newEditModel);
}
but I'm not sure that this is a good idea. Is it? Are there other solutions besides AJAX?
I'm going to tackle your specific pain points one-by-one and I'll try to present my own experience and likely solutions along the way. I'm afraid there is no best answer here. You just have to pick the lesser of the evils.
Rebuilding Dropdownlists
They are a bitch! There is no escaping rebuilding them when you re-render the page. While HTML Forms are good at remembering the selected index (and they will happily restore it for you), you have to rebuild them. If you don't want to rebuild them, switch to Ajax.
Rebuilding Rest of View Model (even nested)
HTML forms are good at rebuilding the whole model for you, as long as you stick to inputs and hidden fields and other form elements (selects, textarea, etc).
There is no avoiding posting back the data if you don't want to rebuild them, but in this case you need to ask yourself - which one is more efficient - posting back few extra bytes or making another query to fetch the missing pieces?
If you don't want to post back the readonly fields, but still want the model binder to work, you can exclude the properties via [Bind(Exclude="Name,SomeOtherProperty")] on the view model class. In this case, you probably need to set them again before sending them back to browser.
// excluding specific props. note that you can also "Include" instead of "Exclude".
[Bind(Exclude="Name,NationalitySelectList")]
public class PersonEditModel
{
...
If you exclude those properties, you don't have to resort to hidden fields and posting them back - as the model binder will simply ignore them and you still will get the values you need populated back.
Personally, I use Edit Models which contain just post-able data instead of Bind magic. Apart from avoiding magic string like you need with Bind, they give me the benefits of strong typing and a clearer intent. I use my own mapper classes to do the mapping but you can use something like Automapper to manage the mapping for you as well.
Another idea may be to cache the initial ViewModel in Session till a successful POST is made. That way, you do not have to rebuild it from grounds up. You just merge the initial one with the submitted one in case of validation errors.
I fight these same battles every time I work with Forms and finally, I've started to just suck it up and go fully AJAX for anything that's not a simple name-value collection type form. Besides being headache free, it also leads to better UX.
P.S. The link you posted is essentially doing the same thing that you're doing - just that its using a mapper framework to map properties between domain and view model.
Is it possible to set an optional [Required] attribute, applicable on PATCH or PUT. I have the following code but no matter what the controller call it will always be required.
public class Car
{
[DataMember(Order = 0)]
public string CarId { get; set; }
[DataMember(Order = 1)]
[Required]
public string IsIncluded { get; set; }
}
Controller;
[HttpPatch]
public HttpResponseMessage PatchCar(Car car)
{
// check if submitted body is valid
if (!ModelState.IsValid)
{
// Something is bad!
}
}
What I want is something like the following;
public class Car
{
[DataMember(Order = 0)]
public string CarId { get; set; }
[DataMember(Order = 1)]
[Required(Patch = True, Put = False]
public string IsIncluded { get; set; }
}
Then my ModelState will take the very into account.
I thought about creating separate derived classes for each action (verb), but the code quickly becomes incredibly verbose.
This is one of the drawbacks of using data annotations for validation unfortunately they cannot be conditionally added.
There are a number of options to you...
Create separate models (or view models) for each verb.
Look into something like this.. http://andrewtwest.com/2011/01/10/conditional-validation-with-data-annotations-in-asp-net-mvc/ which extends required to be IfRequired and adds conditional validation to data annotations. (You would need to roll your own I should think and it may get clumsy!)
Try something like FluentValidation.
http://fluentvalidation.codeplex.com/ (this could be a good option depending on your application requirements).
Hope this helps!