Using EF Core 1.1 and Asp Core 1.1 on Mac
With a model like
public class Model {
public int? Id { get; set; }
public string OtherProp { get; set; }
}
And an action
public class ModelController
{
public Model Get(int? id)
{
DbContext.Set<Model>.Find(id)
}
}
Accessing the url /model/10 fails with a message The key value at position 0 of the call to 'DbSet<Model>.Find' was of type 'int', which does not match the property type of 'Nullable<int>'
The error message is clear but my question is if there is a way to make this work, it seems like a very common use case and one that used to work in previous versions.
I tried casting id to int? but it didn't work. Any ideas?
Also, in case it's useful this is the line that's breaking in EF
Looking at the code you've already linked: this cannot work, since the CLR gives you always the underlying type for a null-able instance. (Don't know why, had similar issues before...)
In code: typeof(int?) != ((int?)1).GetType().
Thus, comparing the type of the property (int?) and the type of the argument will always fail. You have to ask the EF Core team to add support for null-able types for that.
Related: Nullable type is not a nullable type?
Related
I am working with a BaseController that is used for a variety of entities. They may have int or string primary keys, represented by <TPk>.
E.g.:
[HttpGet]
public ActionResult Create(TPk id)
{
return View();
}
Everything is fine until I try and use TPk as an optional parameter.
[HttpGet]
public ActionResult Create(TPk id = default(TPk))
{
return View();
}
It seems that the 'optional' part isn't working.
So /controller/create/2 is fine, but /controller/create gives me the following error:
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Create(Int32)'
The optional works fine with an int or string id. I can call /controller/create/2 AND /controller/create.
But using a generic type argument TPk, the parameterless route no longer works.
What I've Tried
I have tried making the TPk parameter nullable, but it won't compile:
The type 'TPk' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable'
I have tried changing the parameter name from id to altId as per this question - no joy
I have tried calling the same method, in exactly the same way, but with non-generic parameters. E.g.:
public virtual async Task<ActionResult> Create(int id = default(int))
This worked fine.
I have tried creating a simple new project to isolate this code. (Shown below). This still gives problems with the parameterless version.
Simple Code Test
Controller
public abstract class BaseController<TPk> : Controller
{
public ActionResult Create(TPk id = default(TPk))
{
return View();
}
}
public class NewsController : BaseController<int>
{
}
Entity Classes
public class BaseDataModel<TPk>
{
public TPk Id { get; set; }
public string Title { get; set; }
}
public class PageDataModel : BaseDataModel<string>
{
public string Content { get; set; }
}
public class NewsDataModel : BaseDataModel<int>
{
public DateTime Date { get; set; }
}
Asp.net conventions are heavily based on reflection. So this might explain the behavior. I have not tested if it realy does not work, but I am sure at this state you already tried to create a new project (POC) to preclude any custom code.
Maybe it can be fixed by looking deeper into the routing (method selection) and ModelBinder source code...
I would just create a different DuplicateRecord action instead.
If you do not understand your method without this comment, it is a good indication, that your current code probably smells anyway. (You are doing to much at the same thing):
// duplicates existing record if id is passed in, otherwise from scratch
Extract the shared things to another method (maybe even a service class) and have for each difference a seperate method.
That said, the idea of a generic CrudController is lovely, I tried this myself some years ago. But in trying so I have introduced all sort of generic parameters, strategy patterns, event delegates to make all possibilities possible.
What happens if you need a join?
What happens if you need a transaction?
How do you handle errors?
What happens if your crud logic needs 1, 2, 3 ... additional parameters to decide what to do?
Soft Delete / Hard Delete?
Cascade Delete / Restrict Delete?
What happens if you ...
I have written so much code, it was blessing to revert to the good old non generic code. And if abstracted away in a service, the ActionMethods realy do not need to get big.
public async Task<IActionResult> CreateProduct(CancellationToken ct, ProductCreateModel model)
{
var result = await _productService.CreateAsync(model, ct);
//create response with some helpers... probably some ActionFilters
}
Generics can work ofcorse in a simple crud mapping where each View has exact one Entity, but it does not scale very well. So beaware and think twice about what you realy want ;)
maybe my question can seem very silly, but i am a beginner and i am doing an exercise from a book about MVC and ASP.NET.
I don't understand the meaning of the second rows:
[LargerThanValidationAttribute(18)]
public VoterAge { get; set; }
because i see that VolterAge has not a type, and i read the message from VS that VolterAge doesn't exist in this context, and i don't understand the meaning of it. What have i to use for VolterAge? A field in a DataBase? What type of data is it?
Thank you to all.
P.S. LargerThanValidationAttribute is an extension of ValidationAttribute.
You're definitely missing a type in that property declaration.
At a guess, given it's 'VoterAge' and by the value supplied in the LargerThanValidationAttribute I would say it's missing int
It should be
[LargerThanValidationAttribute(18)]
public int VoterAge { get; set; }
This declares that property to be of type int
Is is possible, to get the new value of RowVersion using the same DbContext, without reloading the entity from the database?
Scenario:
Load data into editor form
Save new values
The row in the table gets updated, new value of RowVersion is generated
However, the saved entity still holds the old value of RowVersion, so the new value can not be passed back to the client
All concurrency control articles are usually concerned only with preventing the update (e.g. see this).
However, in the example from the article, a successful update is followed by a redirect to page, where the saved entity is read again and now it has a new RowVersion value. I would like to avoid this redirect.
Thanks to grennis, I found out the source of my problems.
I defined the interface and an entity like
public interface IRowVersion
{
// Attention: this will not be "inherited" by the implementing class !!!
[Timestamp]
byte[] VersionStamp { get; set; }
}
public class Directory : IRowVersion
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
// If this attribute is missing here, then row version is used
// My initial version was without this attribute
[Timestamp]
public byte[] VersionStamp { get; set; }
}
In my problematic version, I thought that having the attribute on the interface property is enough. However, the attribute must be explicitly applied on the entity's property. Otherwise it will not be used at all (not even as the part of update SQL statement). The value was updated only because the DB updates the column value automatically and of course, at next read, I got the new value.
Not entirely related to the problem, but still worth mentioning... The following is really a killer feature of EF6
ctx.Database.Log = s => Debug.Write(s);
SQL Profiler, it was nice knowing you :-)
I have an asp.net MVC application that was recently upgraded from 1.0 to 2.0. I use a Linq-to-Sql data model and in a lot of cases, I have been using these as my model objects, as it was simple and seemed to work...
I have a class that has foreign key relationships with two child tables - these child tables will not always be populated (i.e. the foreign key is nullable).
My code (a little simplified) looks something like this:
/// This would be the generated linq-to-sql class
public class ModelObject
{
//Bunch of properties
public ChildObject { get; set; }
public ChildObject2 { get; set; }
}
public ActionResult Edit(int ID)
{
//Get the current saved object
ModelObject test = _service.GetModelObject(ID);
UpdateModel(test);
}
Since the upgrade to 2.0, I've found that the updateModel call has been instantiated the two child objects - my save then fails, as some of these have empty fields which are not nullable. This wasn't happening previous to the upgrade.
Is there a way to stop this from happening (or does anybody have a pointer as to why this has started to happen since the upgrade)?
You can stop this from happening by specifying the properties you want to exclude from binding as a parameter in your UpdateModel() call:
UpdateModel(test, null, null, new [] { "ChildObject", "ChildObject2"});
You can get more information from MSDN.
This has been driving me nuts for a week now.
I have a class that looks like this:
public class SuggestionVote
{
public virtual int ID { get; set; }
public virtual Suggestion Suggestion { get; set; }
public virtual User User { get; set; }
public virtual VoteTypeWrapper VoteType { get; set; }
public virtual DateTime DateVoted { get; set; }
// Equality overrides omitted
}
VoteTypeWrapper is actually an enum wrapper based on an article on how to fake enums in Entity Framework 4 and looks like this:
public class VoteTypeWrapper
{
private VoteType _type;
public int Value
{
get { return (int)_type; }
set { _type = (VoteType)value; }
}
public VoteType EnumValue
{
get { return _type; }
set { _type = value; }
}
public static implicit operator VoteTypeWrapper(VoteType voteType)
{
return new VoteTypeWrapper { EnumValue = voteType };
}
public static implicit operator VoteType(VoteTypeWrapper voteTypeWrapper)
{
return voteTypeWrapper == null ? VoteType.NotVoted : voteTypeWrapper.EnumValue;
}
}
with the VoteType enumeration being:
public enum VoteType
{
Up,
Down,
NotVoted
}
I've also defined a ComplexType in the model designer:
<ComplexType Name="VoteTypeWrapper" >
<Property Type="Int32" Name="Value" Nullable="false" />
</ComplexType>
The voting system I'm implementing works somewhat like StackOverflow's voting system: The user can vote up or down; voting a second time undoes the previous vote, and voting in the opposite direction (i.e., down when previously voted up) undoes the vote as well.
Now for the problem. Voting once works like a charm and all the values are correctly saved to the database. Undoing a vote, however, refuses to work. To undo a vote I basically mark the vote to undo for deletion and then call SaveChanges on the context.
As soon as I do that an InvalidOperationException occurs giving me the following message:
The entity of type 'System.Data.Entity.DynamicProxies.SuggestionVote_4A3949F5B95E9A51567509467230FD7CEA0FB7761C3AC9C8C2BBC62BCAA033AF'
references the same complex object of type 'Web.Model.VoteTypeWrapper' more than once.
Complex objects cannot be referenced multiple times by the same entity.
I just don't get it. Down anyone know what I could be doing wrong? I've been Googleing for day but to no avail
Well, I've finally decided to work around it but simply mapping an int property instead of a ComplexType. I have also added a (non-mapped) helper property to avoid having to cast constantly from int to VoteType.
I would still love to get an answer for my problem so if you can help it I would appreciate it. I'll give it a couple of days before I mark my own answer as correct.
EDIT: Since I've gotten no answer whatsoever to this, m marking my own answer as good.
I've just encountered the same problem. I have a class that contains a complex type that contains another complex type. Let's call them Class1, Complex1 and ChildComplex. My scenario is the following (I don't know if it matches yours, but the error message is exactly the same).
I retrieve from the context an instance of Class1 and perform a change in a property of Complex1. I then call SaveChanges to the context and get the same exception :
The entity of type '<Class1>' references the same complex object of type '<Complex2>' more than once.
Complex objects cannot be referenced multiple times by the same entity.
I have not found a decent workaround other than cloning the Complex1 instance, replacing the cloned version in the Class1 instance and then making the change. That way EF does not complain that it's the same complex object.
This is a really strange behaviour. If I have time (around 2038) I'll try to isolate it and report it to MS, smells like a bug...
I've just been searching for this same issue, but I've just realized I was misreading the error. Its not complaining that your entity has two properties of the same type, its complaining that your storing the 'exact' same object of that type in both properties.
In my case I was doing a lookup on an table to get my complex type, and I now realize that if the lookup returned the same value it would be the same object.
I fixed this by creating a new object of my complex type and setting its values to the same as the lookup.