Getting "Cannot insert the value NULL into column" when trying to save with .Add() method using DbContext . Please check my POCO's and save method - asp.net

Used code first and everything appears to work apart from the below which also worked before when I used ObjectContext and called context.PCBuilds.AddObject(pcBuild) but after switching to DbContext it's giving me the error.
EFDbContext context = new EFDbContext();
public ActionResult Index()
{
PCBuild pcBuild = new PCBuild();
pcBuild.BuildID = 34245;
pcBuild.BuildName = "Test99";
pcBuild.MarkUp = 25;
pcBuild.BDetails = new List<BDetail>();
context.PCBuilds.Add(pcBuild);
//repository.PCBuilds.Attach(pcBuild);
context.SaveChanges();
return View();
}
Giving me the: Cannot insert the value NULL into column 'BuildID', table 'C:\USERS\ADMIN\DOCUMENTS\VISUAL STUDIO 2010\PROJECTS\NEOCART\NEOCART.WEBUI\APP_DATA\NEODBX.MDF.dbo.PCBuilds'; column does not allow nulls. INSERT fails. Where as BuildID was clearly set before the SaveChanges is called. Appears that calling the .Add(pcBuild) doesn't add the populated object for some reason and when savechanges is called it attempts to insert an empty PCBuild ?
Here are the POCO's
public class PCBuild
{
[Key]
public int BuildID { get; set; }
public string BuildName { get; set; }
public string Socket { get; set; }
public decimal? MarkUp {get; set;}
[InverseProperty("PCBuild")]
public virtual ICollection<BDetail> BDetails { get; set; }
}
public class BDetail
{
[Key]
public int LineID { get; set; }
[ForeignKey("PCBuild")]
public int BuildID { get; set; }
[ForeignKey("Product")]
public int ProductID { get; set; }
public bool? IsSelected { get; set; }
[InverseProperty("BDetails")]
public virtual PCBuild PCBuild { get; set; }
[InverseProperty("BDetails")]
public virtual Product Product { get; set; }
}

Use StoreGeneratedAttribute on the PCBuild.BuildID property. It is not only a key but IDENTITY field.
UPDATE
Actually, it should be [DatabaseGenerated(DatabaseGenerationOption.Identity)] annotation. The article linked above describes early CTP version.
UPDATE 2
Wait, the key is being generated by the app, it is not an identity column in database? Change annotation to [DatabaseGenerated(DatabaseGenerationOption.None)], re-create the context and rebuild the application.

I'm not really familiar with the Code First approach, but could it be that when you specify the BuildID as being a [Key] field, it is setting it as an auto identity field in the database?
As such it may be blocking your attempt to write to it.
Try removing the [Key] identifier, then recreate the database. Can you then save the object ok?

Related

Entity Framework Core 2.2.1 DbQuery error system.Data.SqlTypes.SqlNullValueException

Trying to work with DbQuery. I need to get non-entity type UserDetailDTO using raw sql. Added DbQuery to the context and call it from controller. But it's generate system.Data.SqlTypes.SqlNullValueException.
My context:
public class TrainingAppDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbQuery<UserDetailDTO> UserDetailDTO { get; set; }
public TrainingAppDbContext(DbContextOptions<TrainingAppDbContext> options)
: base(options)
{
Database.EnsureCreated();
}
}
Controller:
public class AccountController : Controller
{
private readonly TrainingAppDbContext ct;
public AccountController(TrainingAppDbContext ct)
{
this.ct = ct;
}
public IActionResult Test()
{
var results = ct.UserDetailDTO.FromSql("SELECT * FROM users").ToList();
return View();
}
}
When I calling my UserDetailDTO from context it's generates an error.
error picture
Take a very close look at the UserDetailDTO class and any other classes that might have foreign keys to this table in your database. We recently found this error was being caused by a [Required] data annotation being added to our entities. In the example below, the [Required] attribute above FirstName is required and there should be no rows in your table where this column is null.
namespace Entities
{
public class UserDetailDTO
{
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string EmailAddress { get; set; }
}
}
In previous versions of EF Core, the [Required] data annotation was ignored. Version 2.2.1 started looking for these annotations in your entities and enforcing them. We had no idea this was mistakenly added to some of our entities a few months back until we updated our EF Core version and started experiencing this error. Since the annotation was incorrectly added in our case, removing the annotation solved our problem. If you find this is the cause for your issue and you actually want the column to be required then you probably have data in your table where this column is null which is what's actually causing the error. Fix that bad data, make that column not null, and the query should start working again.
For those who have fields that can be nullable like DateTime, any Enums, make sure to keep it as a nullable field if your query returns null for those fields. Eg.
public DateTime? DateOfBirth { get; set; }

EF Core - Identity field suddenly jumped by 1000 and not able to search with Id

I am having a problem with primary key/Id of one table in a project.
In this table, the Id suddenly jumped from 38 to 1039.
Now the real issue is, when i find the entity by 1039 it doesn't exists but finding it by 39 gives me the entity.
I am not sure about this behaviour and hence not able to find the solution.
My model
public class Domain : Entity
{
public string Name { get; set; }
public string Description { get; set; }
}
public abstract class Entity
{
public int Id { get; set; }
public DateTime InsertDate { get; set; }
public DateTime? UpdateDate { get; set; }
public DateTime? DeleteDate { get; set; }
public bool IsDeleted { get; set; }
}
Method is like this...
public async Task<Response> Delete(int id)
{
var domain = await DataContext.Domains.FindAsync(id);
if (domain == null)
{
return new Response(ResponseType.NotFound);
}
}
Can anyone please help ?
it depend from Database setting IDENTITY-CACHE.
Identity cache store some values of a identity columns in case of SQL CRASH during a transaction or similar.
To avoid gaps in an identity column, you need to set IDENTITY-CACHE to OFF running this command on a SQL query window:
ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFF
GO
you will find more informations here:
https://social.technet.microsoft.com/wiki/contents/articles/40666.sql-server-2017-identity-cache-feature.aspx

Entity Framework shows inconsistent behaviour when used with Asp.net Identity

I have 3 tables Violation,Comment and and auto generated AspNetUsers respectively.The relationship between them as follows.
I am using code-first approach and my models are as follows.Some properties are removed for brevity.
Violation Model
public class Violation
{
public Violation()
{
this.Comments = new HashSet<Comment>();
}
public int Id { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ApplicationUser CreatorUser { get; set; }
}
Comment Model
public class Comment
{
public int Id { get; set; }
[Required]
public string Content { get; set; }
[Required]
public DateTime PostedDateTime { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public Violation Violation { get; set; }
}
ApplicationUser(AspNetUsers Table)
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
this.Comments = new List<Comment>();
this.Violations = new List<Violation>();
}
public virtual List<Comment> Comments { get; set; }
public virtual List<Violation> Violations { get; set; }
}
The problem is that when I try to retrieve Comment's ApplicationUser navigation property , I see many of them pointing to a null property even database has proper record for each of them.
Shortly,EF doesn't retrieve database records properly.I stuck with it,can't find the reason.
In fact, it's not being lazy-loaded. You didn't add the virtual keyword to your Comment.ApplicationUser property, so Entity Framework cannot override it to add the lazy-loading logic. As a result, it's always going to be null unless you explicitly load it. Add the virtual keyword, and you'll be fine.
If you want the navigation properties populated you need to include them in the query:
var comments = context.Comments
.Include(c => c.Violation)
.Include(c => c.ApplicationUser)
.Where(x => x.Violation.Id == violationId);
https://msdn.microsoft.com/en-us/data/jj574232.aspx#eager

XMLserializer, Entity Framework : Cannot serialize member of type ICollection see inner exception for more details

I want to map XML elements into my database table (using Entity Framework):
var xmlSerializer = new XmlSerializer(typeof(Participant), new XmlRootAttribute("participant"));
var participant = (Participant)xmlSerializer.Deserialize(new StringReader(content));
I have Participant table which I can access by
[XmlRoot("participant", Namespace = "")]
public partial class Participant
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Participant()
{
this.GroupParticipant = new HashSet<GroupParticipant>();
this.ParticipantAddress = new HashSet<ParticipantAddress>();
this.ParticipantPublisher = new HashSet<ParticipantPublisher>();
this.ParticipantJob = new HashSet<ParticipantJob>();
this.ParticipantProvider = new HashSet<ParticipantProvider>();
}
[XmlElement("firstName")]
public string FirstName { get; set; }
[XmlElement("lastName")]
public string LastName { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
//[XmlElement("address")]
//[XmlElement("address")]
//[XmlArray("HashSet<ParticipantAddress>"), XmlElement("address")]
//[XmlArrayItem("ICollection<ParticipantAddress>")]
//[XmlAttribute(DataType = "ICollection<ParticipantAddress>", AttributeName = "address")]
[XmlElement("address", typeof(List<ParticipantAddress>))]
public virtual ICollection<ParticipantAddress> ParticipantAddress { get; set; }
}
ParticipantAddress is ICollection:
[Serializable]
[XmlInclude(typeof(HashSet<ParticipantAddress>))]
public partial class ParticipantAddress
{
public int ParticipantAddressId { get; set; }
public int ParticipantId { get; set; }
[XmlElement("city")]
public string City { get; set; }
[XmlElement("state")]
public string State { get; set; }
[XmlElement("zipCode")]
public string ZipCode { get; set; }
public virtual Participant Participant { get; set; }
}
Exception says:
{"There was an error reflecting type 'x.Participant'."}
My inner Exception says:
{"Cannot serialize member 'xParticipant.ParticipantAddress' of type 'System.Collections.Generic.ICollection`1[[x.ParticipantAddress, APS.Data.BatchInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]', see inner exception for more details."}
I am reading XML by streamReader.
I have tried
[XMLArray]
Changing ICollection to List
make class serializable
Is there any other way to overcome this problem or any examples related to my question or any changes I need to implement in my code?
ICollection is not serializable.
- You can use DTO.
- You can change the collection type (i.e. with List<>) and with XML serialization attributes avoid circular references and/or disable lazy load (i.e. use eagerly load using Include method) or the risk is that you serialize the whole database.
You have this issue because of the virtual properties. You try to serialize a class which has a reference to another class, which has a reference to the first, class, which... endless loop.
If you want to serialize an entity, the best thing you can do is use a DTO class, which is a class used only to export your data. In these classes you can't have virtual properties, but what you can do is include the DTO objects of your ParticipantAddress.
The other thing you can try, if it isn't a necessity to serialize to XML, is use the Newtonsoft.Json package to serialize the entities. The package has some options to deal with navigational properties.
I have created a region and change ICollection<> to List<> because
ICollection is an interface and interfaces are not serializable.
But List<> is a class and this class implements all the below interfaces:
IList, ICollection, IList, ICollection, IReadOnlyList, IReadOnlyCollection, IEnumerable, IEnumerable.
I kept both Icollection as well as List and put [XmlIgnore] on ICollection.
[XmlRoot("participant", Namespace = "")]
public partial class Participant
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Participant()
{
this.GroupParticipantList = new List<GroupParticipant>();
this.ParticipantAddressList = new List<ParticipantAddress>();
this.ParticipantPublisherList = new List<ParticipantPublisher>();
this.ParticipantJobList = new List<ParticipantJob>();
this.ParticipantProviderList = new List<ParticipantProvider>();
}
[XmlElement("firstName")]
public string FirstName { get; set; }
[XmlElement("lastName")]
public string LastName { get; set; }
[XmlIgnore]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ParticipantAddress> ParticipantAddress { get; set; }
#region Custom properties
[XmlElement("address")]
public virtual List<ParticipantAddress> ParticipantAddressList { get; set; }
#endregion
}
But with this option I am having another Problem: If I do any single change in my SQL database and if I do update model from database, then I lose manually implemented code like all the XML sentences in this code.
I answered this in the below article to add [System.Xml.Serialization.XmlIgnore] to entity.tt template
Prevent Property from being serialized
I had a similar issue using EF, to implement a Web service and couldn't serialize the ICollection<> object.
I hopes this helps you out.
public class User
{
public User()
{
sessions = new HashSet<Session>();
}
public int Id { get; set; }
public string Name { get; set; }
[XmlIgnore]
[IgnoreDataMember]
public virtual ICollection<Session> sessions { get; set; }
}
public class Session
{
public int Id { get; set; }
public Datetime start_dtime{ get; set; }
public Datetime end_dtime{ get; set; }
public virtual User user{ get; set; }
}

How to have default values for an attribute in Code First?

Basically I'm trying to expand on the already created SimpleMembership of ASP.NET MVC 4.
I wanna add a balance field that has the initial value of 0.
I've tried with the following code and inserting nothing in the Balance field:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string RealName { get; set; }
public int Balance { get; set; }
}
But unfortunately I get an exception when I try to create a new user (without a balance) that I cannot insert null in balance since it doesn't allow nulls.
How can I get balance to be 0 as a default value in Code First.
The
int Balance
does not allow nulls but
int? Balance
does.
If you want to controll the structure at the database level, use explicit migrations and manually tweak the generated migration code so that you set up a default value at the database level. If you want a default value at the code level, just set the value in the default constructor of the class.
First: You can use a constructor like this:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string RealName { get; set; }
public int Balance { get; set; }
public UserProfile()
{
this.Balance = 0;
}
}
Second, clean and rebuild solution, then use the migration command
Update-database -verbose
Integers have a default value of 0 by default. When you instantiate a new UserProfile, Balance should already be set to 0.
See: Default Values Table (C# Reference)

Resources