I noticed a validation error when changing my WebApi project to use .NET 6 instead of .NET Core 3.1 for my array query parameter types. Previously they didn't return a validation error, but with 6.0 they do.
I have the following class for a query:
public class Query
{
public List<string> Id { get; set; }
}
And a controller with the following endpoint:
[HttpGet()]
public IActionResult Get([FromQuery] Test2 test)
{
return Ok()
}
When I send a query without any query parameters, I get a BadRequest marking the field id as required, when implemented using .NET 6. When using .NET Core 3.1 (or 5.0), the method executes correctly.
For reproduction I have created an ASP.NET Core WebApi project both with .NET Core 3.1, 5.0 and 6.0 and added a controller endpoint with a Query-Entity containing a string array as in the example above without any other changes.
Frankly I'm a bit stumped with this one. I tried to execute the Validator for the System.ComponentModel manually, but this yielded in the expected result (no error). I didn't add anything to the scaffold which would account for this behavior, it's basically identical. I didn't find any mention of this in the list of breaking changes for .NET 6.0 as well.
Adding a custom Validation Attribute ("NotRequired") to the Id property fixes the behavior, I would however prefer to be able to keep the query model as-is as it worked previously and the ComponentModel doesn't show any errors.
From the docs:
The validation system treats non-nullable parameters or bound properties as if they had a [Required(AllowEmptyStrings = true)] attribute. By enabling Nullable contexts, MVC implicitly starts validating non-nullable properties or parameters as if they had been attributed with the [Required(AllowEmptyStrings = true)] attribute.
You can make the property to be nullable reference type so it will make Id optional:
public class Query
{
public List<string>? Id { get; set; }
}
Or you can completely disable nullable context analysis which will also revert to the old behaviour (so personally I would recommend against it).
Related
I am working on an ASP.NET Core MVC project. Currently, I'm trying to add actors from Create.cshtml view. The view has three fields and all of them are required:
ProfilePictureURL
FullName
Bio
I'm making these fields required with some data annotation attributes in the model.
In the controller, I am using [Bind] data annotation in the parentheses of the respective action method. Inside of its body I'm checking !ModelState.IsValid which is expected to be false. On the contrary, it's true and I don't get it why.
Here are some screenshots of model, controller and view.
Controller:
Model:
View:
Can anyone tell me why my !IsModelState.IsValid is always true?
Thanks in advance 😊
Inside of its body I'm checking !ModelState.IsValid which is expected
to be false. On the contrary, it's true and I don't get it why.
Altough, you haven't shared your code snippet in correct way and your asp.net core version information as well. However, if you use older version than asp.net core 6 in that case you might encounter this issue. Because, in older version of asp.net core when you use DataAnnotations in that scenario the property without any annotation doesn't take into account. In your case
public List<Movie> Movies { get; set; } has no DataAnnotations which would comletely be ignored in older than asp.net core 6.
Debug in asp.net core 6:
Note: As you can see in asp.net core 6 as you haven't use any DataAnnotations on Movies property therefore, by default it will consider as [Required] and finally !ModelState.IsValid will be always false. You can check here
Debug in asp.net core Older: 3.XXX:
Note: Here !ModelState.IsValid always true because in older version it will consider as nun-nullable and always consider true. You can check here
Solution:
You should redefine your public List<Movie> Movies { get; set; } and tell what kind of behavior should be expected here. But in dot net core 6 or higher version, it will autometically consider as false not matter you use dataAnnotation
I am trying to create a Controller in an Azure Mobile Services project with a Patch method that takes my entity, structured like this:
public class Page
{
public Page() { }
public int Color { get; set; }
public Point Offset { get; set; }
}
where the class Point looks like this:
public class Point
{
public Point() { }
public double X { get; set; }
public double Y { get; set; }
}
The model, created with CodeFirst strategy using EntityFramework, produces a table with the columns Color, Offset_X and Offset_Y.
Unfortunately the Offset_X and Offset_Y columns are not updated when the Patch method is invoked (while they are correctly populated in the Post method).
My PATCH request body looks like this:
{
"Color":16755404,
"Offset": {
"X":20.0,
"Y":40.0
}
}
I am using the following NuGet packages:
Microsoft.Data.OData.5.8.3
Microsoft.Data.Edm.5.8.3
Microsoft.AspNet.WebApi.OData.5.7.0
Microsoft.Azure.Mobile.Server.2.0.0
How can I make my controller update such properties? Or should I work on the model of the entity?
Any help is very appreciated!!
NOTE: this answer is for OData v4 provided by the Microsoft.OData.Core nuget package and does not apply to Azure Mobile Services
As it is currently implemented in Microsoft.Data.OData, you cannot patch nested entities, but you can patch properties on an entity that are complex types.
The good news for your model is that Point has already been implemented as a mapped complex type in your Entity Model, but the behaviour you are describing leads me to believe that the OData Model does not share the same interpretation
For your example this is a possible odata model builder statement:
var builder = new ODataConventionModelBuilder();
// Complex Type Overrides
builder.ComplexType<Point>();
// EntitySet Declarations
builder.EntitySet<Page>("Page");
It is important when you want to override default interpretations of classes in your model that you declare them before your standard EntitySet declarations, otherwise when Page is declared as an EntitySet, it's properties are immediately included into the model, and when you try to declare Point as a Complex Type you will get a runtime error similar to this:
The type '< your namespace >.Point' cannot be configured as a ComplexType. It was previously configured as an EntityType.
According to your description, I also tested this issue on my side and could encounter the same issue as you provided. Also, I found that we could provide the nested object Point with a new id property for inserting as follows:
TEST:
How can I make my controller update such properties? Or should I work on the model of the entity?
Per my understanding, you could modify the action for updating in your Page TableController and check the nested object Point under Delta<Page> patch, update the Point table if the specific Point record exists. But I would prefer handle updating against each table individually. You could follow adrian hall's book about Relationships. Moreover, you could build your Custom HTTP Endpoints and implement the additional logic by yourself.
I am creating a new website with Asp.net Core 2.0 MVC
In an webforms app I am used to doing something like this with my custom objects
Session["security"] = mySecurity;
mySecurity = Session["security"];
Now my issue is that the good old Asp.net session does not exist in Core. Following the example here https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?tabs=aspnetcore2x I have done:
Added the Microsoft.AspNetCore.Session (2.0.1) nuget packange
Setup my Startup.cs to have the app.UseSession() and .AddSessionStateTempDataProvider()
Added the extension methods listed to serialize and deserialize any object using Json
I have been able to successfully store and retrieve the Json string.
The problem is that any of my private setters do not get restored.
public class Security
{
public userName { get; set;}
public userLevel { get; private set;}
}
In this scenario the user name would be restored but the user level would not. I don't like it, but for the time being I have changed all of my members to just be public, but there has to be a better way. In this case it is my own object in which I can update, but if I wanted to store an object that is part of a library I can't just change the members to public.
This seems like a huge problem, but I have not been able to find an official solution. The only other thing I can think of is to try and convert my object to/from a byte array but I see articles stating not to do that.
I would love to know if anyone has found a good solution to this.
I have a view model,and one of the properties of the view model is a an object called Profile. One of the properties of Profile is a list of another object, called CD. From the view, I set the POST body values to the following
Profile.CD[0].Prop1=TEST&Profile.CD[0].Prop2=TEST&Profile.CD[1].Prop1=TEST2&Profile.CD[1].Prop2=TEST2
If I were to add a third object to the list in the view, which would get posted as Profile.CD[2].Prop1=TEST3 , in the controller all of the sudden Profile.CD is null. 2 items and below Profile.CD gets the values I would expect. As soon as I add in that third item the model binder stops working. I'm at my wits end, and I've tried everything I can think of.
Things I've tried
Removing an item from the view, and adding a new -- WORKS
Removing both from the view, and adding 2 new items -- WORKS
Adding a third item in the view -- FAILS Profile.CD is null in the view model
I'm not using any model state validation rules. When debugging, I've tried something like the following in the immediate window ?Request.Form.Where(x => x.Keys.Contain("Profile.CD")).ToList()) and sure enough, all of my values are present in the Request object even though the list is null in the view model.
The values of the objects in Profile.CD do not have to be unique.. I've tried setting every single value to "TEST" just to verify it's not the input causing this problem.
I'm truly lost..
View Model
public class PortalViewModel {
public ProfileModel Profile { get; set; }
}
Profile Model
public class ProfileModel {
//bunch of other static properties that are bound just fine.. like strings and decimals...
public List<CDModel> CD { get; set; }
}
Controller
public async Task<IActionResult> Edit (PortalViewModel Model)
{
Repo.UpdateProfile(Model.Profile); // in this method it foreachs Profile.CD , but sometimes this is null and other times it get's it's values....
return Ok();
}
I have not been able to find a coding solution to this problem, and started exploring other avenues. I have fixed the problem, but it makes very little sense.
I upgraded the project from .net core 1.0.1 to .net core 1.0.5 , and everything is working 100% of the time. In this upgrade, I made no code changes what so ever. Just upgraded .net core. Very very odd..
I am currently migrating an ASP.NET webform into ASP.NET Core. I have a small issue with a specific Find method. I am quite new to .NET so I hope that the code would explain it better.
var env = Request.Headers.GetValues("environment").First();
var db = new FormsContext(env);
var request = db.InfoRequests.Find(Convert.ToInt32(id));
I am specifically trying to find a replacement for Find(Convert.ToInt32(id)); part of the code where "id" comes in as a string to the method.
To be more specific, in .NET Core they removed System.Data.Entity and this find method used to come from there. (This is from ASP.NET, not .NET Core)
System.Data.Entity.DbSet<InfoRequest>.Find(params object[] keyValues);
however the DbSet method that I am using now is from following: (This one is from .NET Core)
Microsoft.EntityFrameworkCore.DbSet<InfoRequest>
but this class does not have a find method that I can use. Or I am unaware of the syntax.
Lastly, a small snippet from how "InfoRequest" class is created:
public partial class FormsContext : DbContext
{
...
public virtual DbSet<InfoRequest> InfoRequests { get; set; }
...
}
I hope I was able to provide enough information, but shortly, I am trying to find a replacement in .NET Core for the "find" method. Or if you can replace the three lines that I have first shared with an alternative, that should suffice as well. Thanks.
AFAIK, The Find method is not part of EF 1.0 however it is marked that it will be part of EF 1.1 as the EF GitHub issue shows. You can find sample implemented extension method here