Duplicate entries in one to many relation - c#

I have two models as seen below.
public class UserModel
{
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Display(Name = "User Name")]
[StringLength(15, MinimumLength = 8, ErrorMessage = "Length Should not be less than 8 characters")]
[Required]
public string UserName { get; set; }
[Display(Name = "QuestionCategory")]
public virtual ICollection<QuestionCategoryModel> QuestionCategoryModel { get; set; }
}
and
public class QuestionCategoryModel
{
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
[Display(Name = "Title")]
[StringLength(15, MinimumLength = 2, ErrorMessage = "Length Should not be less than 2 characters")]
public string Title { get; set; }
[Required]
public virtual UserModel User { get; set; }
[Required]
public bool IsDeleted { get; set; } = false;
}
The issue am running into is, when i try to insert data to QuestionCategory.
_DbInstance.QuestionCategories.Add(category);
_DbInstance.SaveChanges();
The issue is, each time am adding QuestionCategories, a new row gets added in User despite data already being there. Could someone guide me ?
update
Category variable is coming to a call to Controller, however, am attaching the User component fetching the value from session.
Category.User = (UserModel)Session["User"];
_QCategories.SaveCategory(Mapper.Map<Degree360.ORM.Dictionary.QuestionCategoryModel>(Category));

The problem here is that you don't attach the user to the context your context here is _DbInstance you can follow this link it will provide more information
Entity Framework Add and Attach and Entity States.
I guess you have a DbSet of UserModel that i will call here Users for my example.
so the solution will be something like that.
_DbInstance.Users.Attach(category.User)
PS : you have to do it before the SaveChanges()

Related

Update models in EF core after DBA change database schema

I need to update my models from database but keep all DataAnnotations in my models because all validations use them and when i force Scaffold-DbContext my database all of DataAnnotations will gone.
Our company is old, As our database, We have a 20 years old database and it will use in more than 40 app, So we can't use EF core migration system and it's our DBA task to update database schema, But in our new project we want to use EF core 3.1.18 so we need keep update our models from our master database which control by our DBA.
I find this command for update our models :
Scaffold-DbContext "Server=OurServer;Database=OurDatabase;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Force -d
This is our custom model after first generated by Scaffold-DbContext command :
[Table("tblUser")]
public partial class TblUser
{
[Key]
[Column("intUserID")]
[DisplayName("UserID")]
public long IntUserId { get; set; }
[Column("intUserGroupID")]
public byte IntUserGroupId { get; set; }
[Required(ErrorMessage ="Please enter name of the user")]
[Column("strName")]
[DisplayName("Name")]
[StringLength(50)]
public string StrName { get; set; }
[Required(ErrorMessage = "Please enter the username for login")]
[Column("strUserName")]
[DisplayName("Username")]
[StringLength(50)]
public string StrUserName { get; set; }
[Required(ErrorMessage = "Please enter a password for the user")]
[Column("strPassword")]
[DisplayName("Password")]
[StringLength(100)]
public string StrPassword { get; set; }
[ForeignKey(nameof(IntUserGroupId))]
[InverseProperty(nameof(TblUserGroup.TblUser))]
public virtual TblUserGroup IntUserGroup { get; set; }
}
As you can see we add Required and DisplayName attribute to our models, In some classes we have even custom attribute for our fields.
After we use the command Scaffold-DbContext with -Force parameter all of our DataAnnotations with be deleted like this :
[Table("tblUser")]
public partial class TblUser
{
[Key]
[Column("intUserID")]
public long IntUserId { get; set; }
[Column("intUserGroupID")]
public byte IntUserGroupId { get; set; }
[Required]
[Column("strName")]
[StringLength(50)]
public string StrName { get; set; }
[Required]
[Column("strUserName")]
[StringLength(50)]
public string StrUserName { get; set; }
[Required]
[Column("strPassword")]
[StringLength(100)]
public string StrPassword { get; set; }
[ForeignKey(nameof(IntUserGroupId))]
[InverseProperty(nameof(TblUserGroup.TblUser))]
public virtual TblUserGroup IntUserGroup { get; set; }
}
The Required and DisplayName attribute are used in whole app but we lose them every time our DBA change the database and we need to update models.
Probably we are not only company which have DBA and all databases are managed by them, So what is the solution?
EDIT :
I tried to add metadata as #PanagiotisKanavos said, But it seems metadata not working in EF core Scaffold-DbContext command, Here it is the class i add in project (All these class are in same namespace) :
[MetadataType(typeof(TblUserMetadata))]
public partial class TblUser
{
}
public class TblUserMetadata
{
[DisplayName("User ID")]
public long IntUserID { get; set; }
[DisplayName("UserGroup ID")]
public byte IntUserGroupID { get; set; }
[StringLength(50)]
[Required(ErrorMessage = "Please enter name of the user")]
[DisplayName("Name")]
public string StrName { get; set; }
[StringLength(50)]
[Required(ErrorMessage = "Please enter the username for login")]
[DisplayName("UserName")]
public string StrUserName { get; set; }
[StringLength(100)]
[Required(ErrorMessage = "Please enter a password for the user")]
[DisplayName("Password")]
public string StrPassword { get; set; }
}
But the result is still same, All custom attributes are gone after Scaffold-DbContext command.

Entity Framework Code First adding record error

Ok let me start with my model:
Contact Method Types:
public class ContactMethodType
{
[Key]
[HiddenInput(DisplayValue = false)]
public Guid ContactMethodTypeGUID { get; set; }
[Required(ErrorMessage = "Please enter a Contact Method Type Name.")]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter a brief description.")]
public string Description { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<ContactMethod> ContactMethods { get; set; }
Contact Methods:
public class ContactMethod
{
[Key]
[HiddenInput(DisplayValue = false)]
public Guid ContactMethodGUID { get; set; }
public virtual ContactMethodType Type { get; set; }
public string CountryCode { get; set; }
[Required]
public string Identifier { get; set; }
public bool IsPreferred { get; set; }
}
Recipient:
public class Recipient
{
[Key]
public Guid RecipientGUID { get; set; }
[Required(ErrorMessage = "Please enter a Recipient's First Name.")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Please enter a Recipient's Last Name.")]
public string LastName { get; set; }
public string Company { get; set; }
public UserGroup Owner { get; set; }
public List<ContactMethod> ContactMethods { get; set; }
public User CreatedBy { get; set; }
public DateTime CreatedOn { get; set; }
public User LastModifiedBy { get; set; }
public DateTime LastModifiedOn { get; set; }
public bool IsActive { get; set; }
}
I have two Contact Method Types already defined:
Email and SMS
Now I am creating a new Recipient, so I add all of the required data to my Recipient Object, and then I call:
context.Recipients.Add(myRecipient);
context.SaveChanges();
What I get is an error that I am tying to add a new ContactMethodType when one already exists. But this is supposed to be a one to many relationship, and I do not want to add a new ContactMethodType, just categorize a new Contact Method(s) for my recipient.
I am not sure when this is happening. Maybe my model is incorrect? Based on what is chosen as the type, I pull that Type object, and set it to the ContactMethod.Type variable. But like I said, instead of just linking it to an existing ContactMethodType, it is trying to re-create it, and since the GUID already exists, I get the error that the record cannot be created because the key (GUID) already exits.
Any ideas?
After discussing this offline with Marek, it boiled down to DbSet<TEntity>.Add(entity) assuming that all entities in the graph being added are new.
From The API docs for Add...
Begins tracking the given entity, and any other reachable entities that are not already being tracked, in the Added state such that they will be inserted into the database when SaveChanges() is called.
Because this model uses client generated keys, meaning that all entities have a key value assigned before they are given to the context, you can't use any of the "smarter" methods (such as DbSet<TEntity>.Attach(entity)) that would inspect key values to work out if each entity is new or existing.
After adding the new recipient, you can use call DbSet<TEntity>.Attach(entity) on each existing entity (i.e. the contact method type). Alternatively, DbContext.Entry(entity).State = EntityState.Unchanged will also let EF know that an entity is already in the database.
You could also look at DbContext.ChangeTracker.TrackGraph(...), see the API docs for more info.

System.Data.SqlClient.SqlException: Invalid column name 'phone_types_phone_type_id'

I'm trying to get information from some of my models that have a foreign key relationships to my main employee model. If I map out each model individually, I can access them like normal with no problems, but I have to visit multiple different web pages to do so.
I'm trying to merge several of my models into essentially a single controller, and work with them this way. Unfortunately, when I try to access these models I get a strange error:
System.Data.SqlClient.SqlException: Invalid column name 'phone_types_phone_type_id'.
After searching through my code, apparently the only location phone_types_phone_type_id appears is in my migration code. I'm incredibly new at C# and Asp.Net in general so any help is appreciated.
Here is the code for my model:
[Table("employee.employees")]
public partial class employees1
{
public employees1()
{
employee_email_manager = new List<email_manager>();
employee_employment_history = new HashSet<employment_history>();
employee_job_manager = new HashSet<job_manager>();
employee_phone_manager = new HashSet<phone_manager>();
this.salaries = new HashSet<salary>();
}
[Key]
public int employee_id { get; set; }
[Display(Name="Employee ID")]
public int? assigned_id { get; set; }
[Display(Name="Web User ID")]
public int? all_id { get; set; }
[Required]
[StringLength(50)]
[Display(Name="First Name")]
public string first_name { get; set; }
[StringLength(50)]
[Display(Name="Last Name")]
public string last_name { get; set; }
[Column(TypeName = "date")]
[Display(Name="Birthday")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime birth_day { get; set; }
[Required]
[StringLength(1)]
[Display(Name="Gender")]
public string gender { get; set; }
[Required]
[StringLength(128)]
[Display(Name="Social")]
public string social { get; set; }
[Required]
[StringLength(128)]
[Display(Name="Address")]
public string address_line_1 { get; set; }
[StringLength(50)]
[Display(Name="Suite/Apt#")]
public string address_line_2 { get; set; }
[Required]
[StringLength(40)]
[Display(Name="City")]
public string city { get; set; }
[Required]
[StringLength(20)]
[Display(Name="State")]
public string state { get; set; }
[Required]
[StringLength(11)]
[Display(Name="Zip")]
public string zip { get; set; }
[Column(TypeName = "date")]
[Display(Name="Hire Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime hire_date { get; set; }
[Column(TypeName = "date")]
[Display(Name="Separation Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime? termination_date { get; set; }
[StringLength(70)]
[Display(Name="Emergency Contact Name")]
public string emergency_contact_name { get; set; }
[StringLength(15)]
[Display(Name = "Emergency Contact Number")]
public string emergency_contact_phone { get; set; }
[Display(Name = "Notes")]
public string notes { get; set; }
public virtual ICollection<phone_manager> employee_phone_manager { get; set; }
[Table("employee.phone_manager")]
public partial class phone_manager
{
[Key]
public int phone_id { get; set; }
public int employee_id { get; set; }
[Required]
[StringLength(15)]
public string phone_number { get; set; }
[StringLength(5)]
public string phone_extension { get; set; }
public int phone_type { get; set; }
[Column(TypeName = "date")]
public DateTime date_added { get; set; }
public bool deleted { get; set; }
public virtual employees1 employees1 { get; set; }
public virtual phone_types phone_types { get; set; }
}
[Table("employee.phone_types")]
public partial class phone_types
{
public phone_types()
{
phone_manager = new HashSet<phone_manager>();
}
[Key]
public int phone_type_id { get; set; }
[Required]
[StringLength(50)]
public string phone_type_name { get; set; }
public virtual ICollection<phone_manager> phone_manager { get; set; }
}
}
And the pertinent code from my view:
#foreach (var item in Model.employee_phone_manager)
{
#Html.DisplayFor(modelItem => item.phone_number);
#: -
#Html.DisplayFor(modelItem => item.phone_type);
<br />
}
EDIT I may have found out the issue, but I'll definitely take more input if there is another option. My solution was to take and add the following: [ForeignKey("phone_type")] directly above this line: public virtual phone_types phone_types { get; set; } in my phone_manager class.
Your issue is that your connection string in data layer and connection string in web layer are pointing to different databases.
e.g.
data layer reading dev database
webapp pointing to test database.
Either update connection strings to point to the same database.
or
Make sure your both database have same tables and columns.
After doing quite a bit more research, it seems like I had a fairly unique issue. I attempted several of the fixes listed both on here and many other sites, but almost nothing seemed to fix the issue.
However, the solution I listed at the bottom of my original post seems to be working, and holding up well, so I believe it to be a fairly adequate solution to my problem.
To somewhat outline what was occurring, MVC EF was attempting to find a fk/pk relationship across two models, but since the column names across the models were different, it wasn't able to map them properly. If I were to trying to get all the emails from email_manager by using the email_types table, it wasn't an issue, but moving backwards, and grabbing the information from email_types from email_manager threw errors.
Since the column names between the two tables are different, EF tried to create a column to house the relationship, but since no such column existed, an error was thrown. To correct this, all that's necessary is to tell EF what the foreign key column actually is, and that is done by using [ForeignKey("email_type")] above the collection that houses the parent model.
So for example, my new email_types and email_manager models were as follows:
[Table("employee.email_manager")]
public partial class email_manager
{
[Key]
public int email_id { get; set; }
public int employee_id { get; set; }
[Required]
[StringLength(255)]
public string email { get; set; }
public int email_type { get; set; }
[Column(TypeName = "date")]
public DateTime date_added { get; set; }
public bool deleted { get; set; }
[ForeignKey("email_type")]
public virtual email_types email_types { get; set; }
public virtual employees1 employees1 { get; set; }
}
[Table("employee.email_types")]
public partial class email_types
{
public email_types()
{
email_manager = new HashSet<email_manager>();
}
[Key]
public int email_type_id { get; set; }
[Required]
[StringLength(50)]
public string email_type_name { get; set; }
public virtual ICollection<email_manager> email_manager { get; set; }
}
I had the similar issue. What happens is that in the database foreign keys are created and it starts mapping both the models and then throws an exception. Best way is to avoid foreign key creation by using [NotMapped] as you could use complex models and also avoid creation of Foreign Key.
You have specify the Database Table using [Table("employee.employees")]. Check your database Table is there have a column that name is phone_types_phone_type_id .It Try to find data of that column but It did not find column then throw this Message. My Problem has solve Check my database database Table.
I'm using nop commerce and to get around my problem I had to use ignore in my database map
Ignore(p => p.CategoryAttachmentType);
In the domain I had
/// <summary>
/// Gets or sets the category attachment type
/// </summary>
public CategoryAttachmentType CategoryAttachmentType
{
get
{
return (CategoryAttachmentType)this.CategoryAttachmentTypeId;
}
set
{
this.CategoryAttachmentTypeId = (int)value;
}
}
I came across the same kind of exception. My solution is to go to the model class and verify the exception given property definition/type where it defines. In here better check the Model class/classes where you define 'phone_types_phone_type_id'.
You are right.
I had similar issue.
Something like this
[ForeignKey("StatesTbl")]
public int? State { get; set; }
public StatesTbl StateTbl { get; set; }
So as you can see, I had kept name 'StateTbl' in the last line instead of 'StatesTbl'
and app kept looking for StateTblID. Then I had to change name to 'StatesTbl' instead. And then it started working well.
So now, my changed lines were:
[ForeignKey("StatesTbl")] <== 'StatesTbl' is my original States table
public int? State { get; set; }
public StatesTbl StatesTbl { get; set; }
These are in the AppDbContext.cs class file
I had an issue where I was getting the same error and I resolved it by deleting the audit trail I had created and creating a new one. I had forgotten to do this when I deleted some columns from the table earlier on.
My problem is that I forgot that I've created several SQL Views in my database.
I've used those views in my ASP.NET C# MVC app.
So when I received error I naturally checked all databases tables but forgot about views in which I didn't add new fields.

Entity Framework enum property is ignored by code first

Okay, the problem is this. I have multiple models that make use of enum properties, in all but one of them I've had no problem with this. In this model however code first does not recognise this property and will not create a column in the respective table. What's bizarre is that I have a very similar model to the non-functioning one that works fine, the only difference being that it's in a different namespace.
I'll add that:
My project is targeting .NET 4.5 (and indeed the functioning and non-functioning models are within the same project).
I'm using Entity Framework 5
My enums are defined publicly in the namespace, not nested within a class.
This eliminates the most common causes of problems when trying to use enums in EF.
Here are the code snippets, first the non-working one:
public enum Commodities
{
Test1,
Test2,
Test3
}
[Table("Suppliers")]
public class Suppliers
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Required]
[MaxLength(7)]
[Display(Name = "Vendor ID")]
public string VendorID { get; set; }
[Required]
[Display(Name = "Supplier Name")]
public string SupplierName { get; set; }
public Commodities Commodity { get; set; }
public bool Visible { get; set; }
}
Now here is one that works fine, only difference being that it's in a different namespace and class file (both sit in the same project and have identical using declarations):
public enum Commodities
{
Test4,
Test5,
Test6
}
[Table("Buyers")]
public class Buyer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Buyer ID")]
[Required]
public string BuyerID { get; set; }
[Display(Name = "Buyer Name")]
[Required]
public string Name { get; set; }
[Display(Name = "Windows Logon ID")]
public string WinUserID { get; set; }
[RegularExpression(#"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage = "Please enter a valid e-mail adress")]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email address")]
public string Email { get; set; }
public Commodities Commodity { get; set; }
[Display(Name = "Active?")]
public Boolean IsActive { get; set; }
}
In the "Buyer" table code first correctly initialising the Commodity column and maps it to the enum in the model. In the "Suppliers" table however it's only initialised with the columns: VendorID, VendorName, and Visible with Commodity no where to be found. My question is why is this happening and what can I do to fix it? I'm at my wits end with this.
EDIT: Seems the issue might have to do with using two enums with the same name. Although the compiler has no complaints about ambiguity (and it shouldn't as they exist in different namespaces) it seems EF doesn't like it. Changing the name of one of the enums causes CF to recognise the property.

Need an ASP MVC database guru to properly initialize relationships

I have a one-to-one relationship between a client and an address. By my understanding I have to tinker with the OnModelCreating method which I have. Right now I'm ready to give my application a go but I need to properly initialize my database; but I'm getting an error.
The main plan is to create a client first and then later on create an address to associate with them.
Here's my context class:
public class VolumeV2Context : DbContext
{
public DbSet<GiftCard> GiftCards { get; set; }
public DbSet<Clients> Clients { get; set; }
public DbSet<Address> Address { get; set; }
// use if you need to drop the database
static VolumeV2Context(){
// use if need to reset the models
// Database.SetInitializer(new DropCreateDatabaseIfModelChanges<VolumeV2Context>());
// use to reset whole database tables
Database.SetInitializer(new DropCreateDatabaseAlways<VolumeV2Context>());
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Clients>()
.HasOptional(j => j.Address)
.WithOptionalDependent()
.WillCascadeOnDelete(true);
modelBuilder.Entity<Address>()
.HasRequired(j => j.client)
.WithRequiredDependent()
.WillCascadeOnDelete(true) ;
base.OnModelCreating(modelBuilder);
}
}
Models
public class Address
{
[Required]
public int Id { get; set; }
[DataType(DataType.Text)]
[Display(Name = "Street Address")]
public string StreetAddress { get; set; }
[DataType(DataType.Text)]
[Display(Name = "Postal Code")]
public string PostalCode { get; set; }
[DataType(DataType.Text)]
public string City {get; set; }
[DataType(DataType.Text)]
public string Province {get; set;}
public virtual Clients client { get; set; }
}
public class Clients
{
[Required]
public long Id { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[DataType(DataType.PhoneNumber)]
[Display(Name = "Phone ")]
public string PhoneNumber { get; set; }
public virtual Address Address {get; set;}
[Display(Name = "Email List")]
public Boolean EmailList { get; set; }
[DataType(DataType.EmailAddress)]
[Display(Name = "E-mail")]
public string Email { get; set; }
[DataType(DataType.Text)]
[Display(Name = "Hair Type")]
public string HairType { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
In my first call to the database, which is in the main index method
return View(db.Clients.Take(25).ToList());
It returns an error saying:
Introducing FOREIGN KEY constraint
'FK_dbo.Addresses_dbo.Clients_client_Id' on table 'Addresses' may
cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or
ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Could
not create constraint. See previous errors.
The problem is that you have bi-directional cascading deletes. In other words, deleting a client will delete an address which will delete a client which will delete an address... you get the picture.
Just looking at your data model, wouldn't it make more sense not to delete the client if his/her address were deleted (people move all the time :-)). Or is there a reason why deleting an address should also wipe out the client (after all, your data model does say address is optional....)
If you comment out the lines:
modelBuilder.Entity<Address>()
.HasRequired(j => j.client)
.WithRequiredDependent()
.WillCascadeOnDelete(true) ;
, or make the WillCascadeOnDelete false, does it work?
Another option is to turn the cascade delete convention off completely...
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
but I don't think that's what you're wanting to do here...

Categories