Entity Framework Many To Many allow duplicates - asp.net

Is it possible to insert duplicate rows in Many to Many relationship? This is my class:
public class EventPaintballBundle
{
[Key, Column(Order = 0)]
public int EventID { get; set; }
[Key, Column(Order = 1)]
public int PaintballBundleID { get; set; }
public virtual Event Event { get; set; }
public virtual PaintballBundle PaintballBundle { get; set; }
[Range(1, Int32.MaxValue)]
public int PersonCount { get; set; }
[Required]
public DateTime Data { get; set; }
}
I want to insert second row of those values. The differences are on Date Date and PersonCount value
EventPaintballBundle xx = new EventPaintballBundle() { PaintballBundleID = 1, EventID = 155, Data = DateTime.Now, PersonCount = 5 };
dc.EventPaintballBundles.Add(xx);
dc.SaveChanges();
I'm getting error while I want to insert a duplicate of two keys.
{"Violation of PRIMARY KEY constraint 'PK_dbo.EventPaintballBundles'.
Cannot insert duplicate key in object 'dbo.EventPaintballBundles'. The
duplicate key value is (155, 1).\r\nThe statement has been
terminated."}
How to solve this problem?

Create a primary key that isn't the combination of :
public int EventID { get; set; }
public int PaintballBundleID { get; set; }
The new primary should be unique and not related to anything that actually exist, this key will exist only to make your model work database wise.
It's a classic mistake : to think your primary key should represent something that exist... NO
I learned it the hard way : even if i think that some combination of existing data will stay forever unique. I don't use it. I ALWAYS create my primary key from my own design, not representing anything real.

Related

Entity Framework Core - optional foreign key

I am creating a web api that needs to return details about vehicles. The first part works fine, just returning data from my vehicles table. Now I have another table which may or may not contain additional data about vehicles in the first table. So when I get vehicle data, I want all of the vehicle data, and any additional data from the second table if it exists, like a left join in SQL.
Here are my classes (very much abridged for readability):
public class Vehicle
{
[Key]
[Required]
public string registrationNo { get; set; }
public string capacity{ get; set; }
public string maxGross{ get; set; }
}
public class VehicleDvlaDetail
{
[ForeignKey("Vehicle")]
public string? registrationNumber { get; set; }
public int? co2Emissions { get; set; }
}
And in my context class OnModelCreating I have (again, very abridged):
modelBuilder.Entity<Vehicle>(entity =>
{
entity.HasOne(dvlaRec => dvlaRec.dvlaDetail).WithMany().HasForeignKey(dvla => dvla.registrationNo);
});
This works fine when there is an associated record in the DVLA table, but that isn't always the case. I need to keep them as separate entities as my API will be required to return details from the DVLA table separately as well. Is there any way to create an optional foreign key, as clearly, what I am doing is wrong.
Friendly advice:
Primary key as a string is not a good practice because of performance issues when data table has lots of data in it.
It would be better if you create your model like this:
public class Vehicle
{
public long Id { get; set; }
public string RegistrationNo { get; set; }
public string Capacity { get; set; }
public string MaxGross { get; set; }
public List<VehicleDvlaDetail> VehicleDvlaDetails { get; set; }
}
public class VehicleDvlaDetail
{
public long? VehicleId { get; set; }
public int? Co2Emissions { get; set; }
public Vehicle Vehicle { get; set; }
}
Vehicle and VehicleDvlaDetail are now connected without additional code in OnModelCreating method and it is possible to fetch vehicles with details like this (this is assuming you have named properties in dbcontext Vehicles and VehicleDvlaDetails):
_dbContext.Vehicles.Include(x => x.VehicleDvlaDetails).ToList();
Also as foreign key VehicleId is nullable - this allows for vehicles not to have any dvla details.
Wow. I spent about 3 hours looking for the answer, just posted the question and came across this:
Create an optional foreign key using the fluid-API for Entity Framework 7
So simple...

Why does EF drop and create the same index?

I have an existing index (IX_ItemImportSummaryId) which was generated from the ForeignKey on ItemImportSummary because that is the default index naming structure. If I try to then explicitly create an index with the same name on the Id field, it will drop the index and then create it again in the migration script.
The funny thing is, if I give it a non-default name instead, it will simply rename the index rather than drop then create it. It almost seems like EF is stupid in this scenario and doesn't realize that it can just not do anything at all.
public class ItemImportSummaryDetail
{
public int Id { get; set; }
[ForeignKey("ItemImportSummaryId")]
public virtual ItemImportSummary ItemImportSummary { get; set; }
[Index("IX_ItemImportSummaryId", IsUnique = false)]
[Index("IX_Mpbid", 2, IsUnique = true)]
public int ItemImportSummaryId { get; set; }
[Required]
[StringLength(maximumLength:10)]
[Index("IX_Mpbid", 1, IsUnique = true)]
public string Mpbid { get; set; }
}
This is what add-migration generates:
DropIndex("dbo.ItemImportSummaryDetails", new[] { "ItemImportSummaryId" });
CreateIndex("dbo.ItemImportSummaryDetails", "ItemImportSummaryId");

How does Entity Framework Core decide which properties are going to be NOT NULL in the database?

I am using Entity Framework to create a simple banking application for learning purposes. I don't understand how it decides which properties will become nullable and which become non nullable columns in the database.
For example this
model
public class Transaction
{
public int Id { get; set; }
public Account From { get; set; }
public Account To { get; set; }
[DataType(DataType.Currency)]
public decimal Amount { get; set; }
public String Currency { get; set; }
public DateTime Timestamp { get; set; }
}
generates the following
SQL
CREATE TABLE [dbo].[Transactions] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Amount] DECIMAL (18, 2) NOT NULL,
[Currency] NVARCHAR (MAX) NULL,
[FromId] INT NULL,
[Timestamp] DATETIME2 (7) NOT NULL,
[ToId] INT NULL,
CONSTRAINT [PK_Transactions] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_Transactions_Accounts_FromId] FOREIGN KEY ([FromId]) REFERENCES [dbo].[Accounts] ([Id]),
CONSTRAINT [FK_Transactions_Accounts_ToId] FOREIGN KEY ([ToId]) REFERENCES [dbo].[Accounts] ([Id])
);
GO
CREATE NONCLUSTERED INDEX [IX_Transactions_FromId]
ON [dbo].[Transactions]([FromId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_Transactions_ToId]
ON [dbo].[Transactions]([ToId] ASC);
Question
Why is Currency nullable but not Amount? And what about the foreign keys FromId and ToId?
Appendix
In case it is relevant, here is the Account class referenced by the Transaction class.
public class Account
{
public int Id { get; set; }
public int UserId { get; set; }
public User User { get; set; }
[InverseProperty("From")]
public ICollection<Transaction> TransactionsSend { get; set; }
[InverseProperty("To")]
public ICollection<Transaction> TransactionsReceived { get; set; }
public decimal Balance { get; set; }
}
There is a set of conventions at work here. You can fine-tune everything but the basic rules are clear and simple:
Why is Currency nullable but not Amount? And what about the foreign keys FromId and ToId?
Amount is a value-type. The C# property can't be null When you change it to decimal? the column will be nullable too.
Currency is a reference-type (string ). The From and To properties are references too. References will map to ALLOW-NULL unless you mark them as [Required]
And DateTime is also a value-type.

Wrong navigation properties when using SQLite and Entity Framework 6

Lately I've been playing around with SQLite using Entity Framework but something is not very clear to me regarding the navigation properties of the generated entities after DB first approach. And more specifically, many-to-many relationships.
Note: Using ASP.NET Web Api OWIN project.
This is what I did:
I installed latest version of Entity Framework
I installed latest version of System.Data.SQLite
I used Firefox add-on to create my database. It generated my *.sqlite
Example for one of my many-to-many db definitions while creating the DB:
CREATE TABLE "Users"
(
"Id" INTEGER PRIMARY KEY NOT NULL UNIQUE ,
"IdSrvId" INTEGER NOT NULL UNIQUE ,
"FirstName" VARCHAR NOT NULL ,
"LastName" VARCHAR NOT NULL ,
"Email" VARCHAR NOT NULL ,
"About" VARCHAR NOT NULL ,
"GenderId" INTEGER NOT NULL UNIQUE ,
"BirthDate" DATETIME,
"PhoneNumber" VARCHAR
)
CREATE TABLE "UserLanguаges"
(
"Id" INTEGER PRIMARY KEY NOT NULL UNIQUE ,
"UserId" INTEGER NULL REFERENCES Users(Id),
"LanguageId" INTEGER NULL REFERENCES Languаges(Id)
)
CREATE TABLE "Langugaes"
(
"Id" INTEGER PRIMARY KEY NOT NULL UNIQUE ,
"Name" VARCHAR NOT NULL UNIQUE
)
After that, I used Visual Studio 2015 to create a Data Model using that *.sqlite file. Following this tutorial: SQLite EntityFramework 6 Tutorial
After the generation I got all of my tables as entities looking like this:
public partial class User
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public User()
{
this.GroupUsers = new HashSet<GroupUser>();
this.UserLanguаges = new HashSet<UserLanguаges>();
}
public long Id { get; set; }
public long IdSrvId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string About { get; set; }
public long GenderId { get; set; }
public Nullable<System.DateTime> BirthDate { get; set; }
public string PhoneNumber { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<UserLanguаges> UserLanguаges { get; set; }
}
public partial class Languаges
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Languаges()
{
this.UserLanguаges = new HashSet<UserLanguаges>();
}
public long Id { get; set; }
public string Name { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<UserLanguаges> UserLanguаges { get; set; }
}
public partial class UserLanguаges
{
public long Id { get; set; }
public Nullable<long> UserId { get; set; }
public Nullable<long> LanguageId { get; set; }
public virtual Languаges Languаges { get; set; }
public virtual User User { get; set; }
}
What worries me here, are the navigation properties inside User and Language entities. As you can see, they make a reference to the 'bridge' table helping for the many-to-many relationship but not directly to the other entity as I expected.
I expected this:
public virtual ICollection<UserLanguаges> UserLanguаges { get; set; }
to look like this:
public virtual ICollection<Languаge> Languаges { get; set; }
inside of the User entity.
How can I fix that?
The only time Entity Framework can omit the join table is if that table consists purely of the keys of the tables being joined in a many-to-many relationship. It's the presence of the Id column on this table that is causing it to generate a new entity.
The only way around this is to remove that Id column and make that table have a composite key consisting of the UserId and LanguageId keys. If you cannot change the database schema, there's no other option but take a deep breath and accept how it works.
Some additional reading on how EF handles many-to-many relationships: https://msdn.microsoft.com/en-us/library/dd742359.aspx

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