EF Code First ApplicationUser required DateTime not added - asp.net-core

I have a problem registering new users with my new ASP.NET Core Application (Code First).
When registering i'm using an `ApplicationUser` inheriting from `IdentityUser`.
public class ApplicationUser : IdentityUser
{
[Required]
[DefaultValue(5)]
public int ViewerRange { get; set; }
[DefaultValue(false)]
[Required]
public bool IsTACAccepted { get; set; }
public DateTime? TACAccepted { get; set; }
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime Created { get; set; }
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime Modified { get; set; }
public string Role { get; set; }
public ApplicationUser()
{
Modified = DateTime.Now;
Created = DateTime.Now;
}
}
HereĀ“s the part of Register.cshtml.cs
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = new ApplicationUser
{
UserName = Input.Email,
Email = Input.Email,
Role = Input.Role,
Created = DateTime.Now,
Modified = DateTime.Now,
IsTACAccepted = false,
ViewerRange = 5
};
var result = await _userManager.CreateAsync(user, Input.Password);
//[...]
When registering, the CreateAsync Method fails with the following error:
InvalidOperationException: The value for property 'ApplicationUser.Modified' cannot be set to null because its type is 'System.DateTime' which is not a nullable type.
I can't understand why this is failing.
Can someone explain in detail please?
Kind regards

The reason for the error is that [DatabaseGenerated(DatabaseGeneratedOption.Computed)] says its value is generated by the database, so any assigned value should be ignored. The error shows this isn't the case though.
This isn't the only oddity in the code. [DatabaseGenerated(DatabaseGeneratedOption.Identity)] on a DateTime makes no sense. IDENTITY is used to autoincrement numeric fields. This has no meaning for a DateTime column.
f you want to specify the values yourself, remove the DatabaseGenerated attributes and use them as normal fields. You can avoid the explicit assignment by specifying a default property value, eg
public DateTime Created { get; set; } = DateTime.Now;
public DateTime Modified { get; set; } = DateTime.Now;
It's also possible to configure the database to generate those values. This is described in the docs, in the Generated Values article. Adding Created and Modified columns is described in the Date/time generation section.
A default value constraint for the Created column can be specified with HasDefaultValueSql :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()");
}
The Modified timestamp needs a trigger though, which can't be specified in a DbContext's model. This must be created in the database, either directly :
CREATE TRIGGER [dbo].[Blogs_UPDATE] ON [dbo].[Blogs]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
DECLARE #Id INT
SELECT #Id = INSERTED.BlogId
FROM INSERTED
UPDATE dbo.Blogs
SET LastUpdated = GETDATE()
WHERE BlogId = #Id
END
Or through a raw SQL migration that executes the same script :
migrationBuilder.Sql(
#"
EXEC ('CREATE TRIGGER [dbo].[Blogs_UPDATE] ON [dbo].[Blogs]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
DECLARE #Id INT
SELECT #Id = INSERTED.BlogId
FROM INSERTED
UPDATE dbo.Blogs
SET LastUpdated = GETDATE()
WHERE BlogId = #Id
END')");

public DateTime? Created { get; set; }
public DateTime? Modified { get; set; }
Use this, your model needs to be told that there can be null values inside your database, otherwise it will assume there aren't.
UPDATE
**I did not actually carefully noticed your error. Now I will update my answer:-**
I see you did not use this property in your controller. so initially this property nullable. for that you found this issue. first of all, use this property to ignore nullable.
Try like this:-
public DateTime TACAccepted { get; set; } = DateTime.Now;
You can try this because your property it's null. You set this property like the above code for ignoring the nullable. or you remove this property from your model if it's don't need. I think it's will resolve your issue.
Also, make sure that column is declared as a DateTime in your SQL Database.

Related

Context.saveChanges() modifies foreign key that has already been set

I have two entities:
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
and
public class VideoModel
{
public int Id { get; set; }
public string VideoFileName { get; set; }
public DateTime UploadedTime { get; set; }
public virtual UserProfile Owner { get; set; }
}
When i trying to persist VideoModel entity, the problem appears:
VideoModel video = db.VideoModels.Create();
video.VideoFileName = fileName;
video.Owner = usersContext.UserProfiles.Find(WebSecurity.CurrentUserId); // CurrentUserId = 3, ok
video.UploadedTime = DateTime.Now; // video.Owner.UserId = 3
db.VideoModels.Add(video); // still 3
db.SaveChanges(); // Problem! video.Owner.UserId = 10
And the new value assigned to UserId by SaveChanges() method is greater than the value assigned in previous attempt on 1. Of course the foreign key constraint is broken. Why the method behaves in such a strange way?
If EntityFramework finds the UserProfile that you have asked it to search for, then the behavior you are expecting is how it should behave as.
Instead what could be happening at the moment is EntityFramework cannot find the UserProfile that you are asking via usersContext.UserProfiles.Find(...); and instead returns null. It then creates a new UserProfile while creating the VideoModel in order to maintain the referential integrity. Since there are no requirements on Username (such as length should be at least 8 characters or more), it is able to create a new user without any exception being thrown anywhere.
In order to test this theory out, query your UserProfile table in the database immediately after your new VideoModel is created. I am pretty certain a new UserProfile is being created. If there is one being created, then please let me know what you find.

MVC An entity object cannot be referenced by multiple instances of IEntityChangeTracker

I've got a model that represents a joint table (with payload) in my database:
public class UserHasCar
{
// Foreign keys
[Key, Column(Order = 0)]
public string ApplicationUserId { get; set; }
[Key, Column(Order = 1)]
public int CarId { get; set; }
// Navigation properties
[Required]
public virtual ApplicationUser ApplicationUser { get; set; }
[Required]
public virtual Car Car{ get; set; }
// Additional fields
public int YearsRidden { get; set; }
}
public class Car
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<UserHasCar> UserHasCars { get; set; }
}
public class ApplicationUser : IdentityUser
{
public int BirthYear{ get; set; }
public virtual ICollection<UserHasCar> UserHasCars { get; set; }
}
I have a form that includes multiple select boxes, and upon submitting I want to clear out all records related to that user who submitted the form in the UserHasCar table and replace them with the new updated information. I'm getting a An entity object cannot be referenced by multiple instances of IEntityChangeTracker. because I am doing something wrong, but I don't see where I am using more than one context. This code happens in my controller:
public ApplicationUser GetCurrentUser()
{
return UserManager.FindById(User.Identity.GetUserId());
}
public string GetUserId()
{
string id = User.Identity.GetUserId();
var user = UserManager.FindById(id);
return user.Id;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ManageCars(FormCollection form)
{
string id = GetUserId();
// Remove cars records with my id from database
var queryCars = (from m in db.UserHasCars where m.ApplicationUserId == id select m).ToList();
foreach (var record in queryCars )
{
// Could the problem be here?
db.UserHasCars.Remove(record)
}
// Add user-submitted cars to the database
string carval = form["Cars[0]"];
Car car = (from m in db.Cars where m.Name == carval select m).First();
int carid = car.ID;
// I get the abovementioned title error here
db.UserHasCars.Add(
new UserHasCar()
{
ApplicationUser = GetCurrentUser(),
ApplicationUserId = id,
Car = car,
CarId = carid,
YearsRidden = 0
}
);
db.SaveChanges();
}
I've seen many SO posts, but can't seem the problem as why my code doesn't want to save the new database entries.
EDIT
The solution was to remove the call to get the user and replace it with a query. Why? I was making database conflict errors by having both types of calls (database and DataManager calls in the same controller action). I ended up using a modified GetUser() function instead of GetCurrentUser()
Code:
public ApplicationUser GetUser()
{
// As opposed to:
// UserManager.FindById(User.Identity.GetUserId())
// We make a database call to grab our user instead
// So we don't get database context conflicts by using UserManager
string id = GetUserId();
return db.Users.Where(m => m.Id == id).First();
}
public string GetUserId()
{
return User.Identity.GetUserId();
}
// snip
// in ManageCars(FormCollection form)
ApplicationUser user = GetUser();
// snip
var newRow = db.UserHasCars.Create();
newRow.ApplicationUser = user;
// snip
db.UserHasCars.Add(newRow);
Try removing this line:
ApplicationUser = GetCurrentUser(),
from your object instantiation when adding.
Entity populates this object automatically once you set the foreign key ApplicationUserId. If UserManager.FindById(User.Identity.GetUserId()) uses a different db context that's where your exception is coming from.
Also to save yourself further trouble down the line, you should always call db.SaveChanges() in between the two operations. If you're worried about the atomicity of the db operation, just wrap the whole thing in a Transaction.
And when adding new rows to a table, I usually prefer to use something like:
var newRow = db.SomeTable.Create();
newRow.SomeColumn1 = "something";
newRow.SomeColumn2 = 5;
db.SomeTable.Add(newRow);
db.SaveChanges();
In order to delete entries from UserHasCars you need to change their EntityState to Deleted.
Example:
foreach (var record in queryCars )
{
db.ObjectStateManager.ChangeObjectState(record, EntityState.Deleted);
}
Hope this will fix your issue.

Querying data from a child table in .Net Mobile service

I have two simple models in .net backend based Azure Mobile Service Project, as shown below & I am not able to query the child table (querying parent table, UserItem, works just fine)
(The Id is nvarchar(128) & is autogenerated as newId by DB)
public class AnswerItem: EntityData
{
public string Content { get; set; }
public UserItem By { get; set; }
public QuestionItem ForQuestion { get; set; }
public double Rating { get; set; }
public string Comment { get; set; }
}
& a child to this UserItem Table as shown below
public class QuestionItem: EntityData
{
public string Content { get; set; }
public bool IsAnswered { get; set; }
public int NumberOfAnswers {get; set;}
public UserItem By { get; set; }
public string ById { get; set; }
public string AtLocation { get; set; }
}
As you notice, the QuestionItem has a FK relationship to UserItem table on ById field (Referencing Id field in UserItem Table)
The issue is I am getting a Bad Request error when I try to query the data from child table
Following are some queries that I tried
private IMobileServiceTable<QuestionItem> questionTable = App.MobileService.GetTable<QuestionItem>();
questions = await questionTable.Where(x=>x.IsAnswered==true).ToCollectionAsync(); (Does not Work)
questions = await questionTable.Where(x=>x.ById="UserIdGoesHere").ToCollectionAsync(); (Does Not Work)
questions = await questionTable.Where(x=>x.Content.StartsWith("q")).ToCollectionAsync(); (This Works)
questions = await questionTable.ToCollectionAsync(); (This Works as well)
If I fire a TSQL query in Sql Server Object explorer they all return correct values.
I am at my wits end on what could be wrong with my approach.
Any help is really appreciated.
Thanks
Supreet
Investigating further the Request it was generating was like this
192.168.2.4:50002/tables/QuestionItem?$filter=(byid eq 'myUniqueGuId')
analyzing fiddler output shows this error
"The query specified in the URI is not valid. Could not find a property named 'byid' on type 'x2Service.DataObjects.QuestionItem'"
Off course there is no fields in the table by the name of 'byid' the one I have is called 'ById' Its the JsonProperty adorner that changed it [JsonProperty(PropertyName = "byid")] In my client class.
Removed the Json Property & it worked just fine

how to use the stored procs using petapoco

I have created a stored procedure in data base.
var db = new PetaPoco.Database("connection name");
return db.Query<sppoco>("exec spstp");
I declared a spppoco.cs in models with the column names I used in stored procedure
public class spppoco
{
public int rid { get; set; }
public string name { get; set; }
public int Address { get; set; }
}
But when I am executing I am getting an error like Invalid object name 'sppoco'.
That's easy! It's just a typo.

this[propertyName] is not a function in breeze.debug.js

I am using Hot towel template and extended functionality of it by using breeze. I have used breeze.partial-entities.js file to conver breeze entities to proper dtos that can be used by knockout observables as shown below.
function dtoToEntityMapper(dto) {
var keyValue = dto[keyName];
var entity = manager.getEntityByKey(entityName, keyValue);
if (!entity) {
// We don't have it, so create it as a partial
extendWith = $.extend({ }, extendWith || defaultExtension);
extendWith[keyName] = keyValue;
entity = manager.createEntity(entityName, extendWith);
}
mapToEntity(entity, dto);
entity.entityAspect.setUnchanged();
return entity;
}
For few of the entities it is working properly and getting breeze data converted to entities but for one of the entity implementation is failing. Model for the same is given as below.
public class StandardResourceProperty
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int StandardResourceId{ get; set; }
public int InputTypeId{ get; set; }
public int ListGroupId{ get; set; }
public string Format{ get; set; }
public string Calculation{ get; set; }
public bool Required{ get; set; }
public int MinSize{ get; set; }
public int MaxSize{ get; set; }
public string DefaultValue{ get; set; }
public string Comment { get; set; }
public virtual StandardResource AssociatedStandardResource { get; set; }
public virtual List AssociatedList { get; set; }
}
The error i am getting is
TypeError: this[propertyName] is not a function
[Break On This Error]
thispropertyName;
breeze.debug.js (line 13157)
]
with code
proto.setProperty = function(propertyName, value) {
this[propertyName](value);
// allow set property chaining.
return this;
};
Please let me know . What can be possible issue with the implementation also , it would be great if i can get more suggestion on how to debug and trace such issues.
Let's back up. I do not understand what you mean by "convert breeze entities to proper dtos that can be used by knockout observables". Breeze entities are already configured as KO observables (assuming you are using the default Breeze model library configuration). What are you trying to do?
I suspect you are following along with the Code Camper Jumpstart course where it does a getSessionPartials projection query. That query (like all projections) returns DTOs - not entities - and maps them with the dtoToEntityMapper method into Session entities.
The CCJS dtoToEntityMapper method cannot be used with entities. It is for converting from a DTO to an Entity and takes DTOs - not entities - as input.
Goodbye to dtoEntityMapper
The dtoToEntityMapper method pre-dates the ability of Breeze to automate projection-to-entity mapping by adding .toType('StandardResourceProperty') to your projection query.
Here is what the CCJS getSessionPartials query could look like now:
var query = EntityQuery
.from('Sessions')
.select('id, title, code, speakerId, trackId, timeSlotId, roomId, level, tags')
.orderBy(orderBy.session)
.toType('Session');
If you go this way, be sure to set the default state of the isPartial flag to true in the custom constructor (see model.js)
metadataStore.registerEntityTypeCtor(
'Session', function () { this.isPartial = true; }, sessionInitializer);
Note that this.isPartial = true is the reverse of the CCJS example where the default was false.
Make sure that you set isPartial(false) when you query or create a full entity. In CCJS there are two places to do that: in the success-callback of getSessionById AND in createSession which would become:
var createSession = function () {
return manager.createEntity(entityNames.session, {isPartial: false});
};