I have an issue that I can't seem to get around. I'm working with renewing an application based on an existing MySQL database. I'm using EF Core 6.0.1 and Pomelo.EntityFrameworkCore.MySql 6.0.0.
I can easily retrieve data from tables so I know the setup is working. However - when I try to map an entity to a table containing a nullable value in MySql, EF Core throws an error:
System.InvalidCastException: Unable to cast object of type 'System.DBNull' to type 'System.String'.
A short example could be this class:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; };
}
Mapped to a table in MySQL:
CREATE TABLE `Users` (
`Id` INT(11) NOT NULL,
`Username` VARCHAR(50) NULL DEFAULT NULL,
`Email` VARCHAR(200) NULL DEFAULT NULL,
PRIMARY KEY (`Id`) USING BTREE
;
I have the following mapping in place:
public void Configure(EntityTypeBuilder<User> builder)
{
builder.ToTable("Users");
builder.HasKey(o => o.Id);
builder.Property(t => t.Username).HasColumnType("varchar").HasMaxLength(50);
builder.Property(t => t.Email).HasColumnType("varchar");
}
In the current example the Username and Email fields can contain null values in the DB (and they do).
If I remove Username and Password from my class, then everything works. But whenever I have a nullable type I try to map, I get the DBnull exception.
I'm very new to using EF with MySQL - so I figure there might be something I'm missing? :)
As per the comments from #GertArnold and #SvyatoslavDanyliv below, I fixed my issue by doing the following:
Ensuring that my csproj file had <Nullable>enable</Nullable>
Set my Username and Email properties to nullable reference types by using string? instead of just string
Solved everything right away.
Thanks guys.
Related
I am working on an application where I am using .Net Core 2, EF Core and MySQL as database server via Code First approach.
I have 2 tables:
User
Employee
User table is the main table which contains the user information and Employee table is the child table which has a column ID_User as shown below:
public class User : BaseEntity
{
public int ID_User { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public virtual ICollection<Employee> Employees{get;set;}
}
public class Employee : Entity
{
public int ID_Employee { get; set; }
public string Name { get; set; }
public int ID_User { get; set; }
public virtual User User { get; set; }
}
Everything works perfectly when I use the above mapping and I have enough data in both the tables.
Now, I want to make the column ID_User in Employee table as nullable
To implement this change I made following change to my model:
public class Employee : Entity
{
public int ID_Employee { get; set; }
public string Name { get; set; }
public int? ID_User { get; set; }
public virtual User User { get; set; }
}
and in mapping file:
builder.HasOne(x=>x.User).WithMany(y=>y.Employees).HasForeignKey(z=>z.ID_User).IsRequired(false);
After running the dotnet ef migrations add empuser command it generated the following migration code:
migrationBuilder.DropForeignKey(
name: "FK_Employee_User_ID_User",
table: "Employee");
migrationBuilder.AlterColumn<int>(
name: "ID_User",
table: "Employee",
nullable: true,
oldClrType: typeof(int));
migrationBuilder.AddForeignKey(
name: "FK_Employee_User_ID_User",
table: "Employee",
column: "ID_User",
principalTable: "User",
principalColumn: "ID_User",
onDelete: ReferentialAction.Restrict);
Now when I run dotnet ef database update it is giving me the following error:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'CONSTRAINT FK_Employee_User_ID_User' at line 1
Please help.
Thanks
Try putting the SQL statements directly into the MySQL Workbench.
Type "dotnet ef migrations script" in your commandprompt.
Copy the generated SQL script.
Paste it into your Workbench.
Check where the errors occur.
When I got similar errors using EF core 2 with MySQL this helped me understand the problem better and helped solve the problem. (for me it was a typing error). You can at least use this method to determine if it is an error in the migrations or in your SQL statements.
I know this is not a concrete solution, but I hope this will help you understand your problem and solve it :)
Have you checked foreign key name in database?
I have found bug from migration builder, where creating new table:
In my case I use EF Core 2.1 Mysql DotNet Connector bug
Where migration builder creates wrongly named foreign key name...
To avoid this wrongly named foreign key name:
fix for migration builder
If your foreign key is named wrongly, you can manually rename it in database and then your migration might work.
The key word CONSTRAINT is not supported for certain version of MYSQL . EF core generates drop constraint for dropping foreign key . I have to change the following:-
migrationBuilder.DropForeignKey(
name: "FK_XXXXX",
table: "XXXXXX");
to
migrationBuilder.Sql("ALTER TABLE XXXXXX DROP FOREIGN KEY FK_XXXXX");
Consider the following database tables:
CREATE TABLE [dbo].[Step]
(
[StepId] INT NOT NULL PRIMARY KEY IDENTITY,
[OrderIndex] INT NOT NULL
)
CREATE TABLE [dbo].[StepInput]
(
[StepInputId] INT NOT NULL PRIMARY KEY IDENTITY,
[StepId] INT NOT NULL,
[OrderIndex] INT NOT NULL,
CONSTRAINT [FK_StepInput_Step] FOREIGN KEY ([StepId]) REFERENCES [Step]([StepId]) ON DELETE CASCADE,
)
With the following POCOs:
public class Step
{
public virtual int StepId { get; set; }
public virtual ICollection<StepInput> StepInputs { get; set; }
}
public class StepInput
{
public int StepInputId { get; set; }
public int StepId { get; set; }
public virtual int OrderIndex { get; set; }
}
I would like for StepInputs to be ordered by OrderIndex. Is there any way to setup the navigation property so that it is sorted automatically by EF, so that the user of Step does not have to call StepInputs.OrderBy every time?
I am using the database-first model.
Edit: I did realize that I could just add an OrderedInputs property to Step, which returns StepInputs.OrderBy(...), which solved the immediate problem, though I'm not sure about the performance implications. I am still curious as to whether there is a way to set this up in EF without having to use a custom property.
No way...! I think you have two tricks:
1) Create a static dbcontext, and for the first time, load the data you need into memory using .load() and perform your ordering, and then just use in-memory data in the Local property of your dbctx.<DbSet>s - an ugly way and most probably doesn't satisfy you...
2) Create a stored procedure to retrieve your data and perform ordering in database, and map that sp to an entity in your model.
I have inherited a sql server database and an ASP.Net MVC 4 web application which is using Entity Framework 5.0 Code First with Auto Migrations . However, it appears the previous developer forgot to add a Primary Key to one of the tables. I am now trying to do this using Auto Migrations, however, it is not working, no errors either, just seems to be ignoring the command.
The table is like this
public int CourseDateHistoryID { get; set; }
public int CourseDateID { get; set; }
public int Event { get; set; }
//public string testProp{ get; set; }
And my mapping is like this to try and create the primary key on CourseDateHistoryID
this.HasKey(t => t.CourseDateHistoryID);
this.Property(t => t.CourseDateHistoryID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
I thought maybe the connection string was wrong or something, so I tried to add a dumby string property called testProp using auto migrations, but this worked fine.
Would anyone have any ideas as to why I cannot set CourseDateHistoryID as the PK using auto migrations?
Thanks for any help.
You can try manually updating the database using Update-Database -verbose command. It should show you the migration it's applying as well as the errors it encounters.
Or why not add another migration using the Add-Migration command and manually add primary key there, for example:
public partial class AddPrimaryKey : DbMigration
{
public override void Up()
{
AddPrimaryKey(table: "dto.table", column: "CourseDateHistoryID", name: "PK_CourseDateHistoryID");
}
public override void Down()
{
DropPrimaryKey(table: "dto.table", name: "PK_CourseDateHistoryID");
}
}
Hope this helps.
Using the Entity Framework tools I reverse engineered code first the POCO classes for a MySQL database into a class library. I created an MVC project that references this class library and I am trying to create a controller with read/write actions and views. After setting the model and data context classes and clicking "Add" I get this error:
EntityType xxx has no key defined. Define the key for this EntityType.
The problem is, in the map it is marked properly:
public UserApplicationMap()
{
// Primary Key
this.HasKey(t => new { t.UserID, t.AppID });
...
I tried doing this in the class:
public class UserApplication
{
[Key, Column(Order = 0)]
public int UserID { get; set; }
[Key, Column(Order = 1)]
public int AppID { get; set; }
But the compiler had a fit with the Column attribute and flagged it as invalid.
So I am a bit stumped. I really don't want to add an auto increment field to this table if I can just use a composite key.
I am using Visual Studio 2012, EF 5, MVC 4, and MySQL connector 6.6.4
I have got this weird error Cannot add an entity with a key that is already in use
But what is quite irritable about that error is that user gets no detais - Who? What? What table? What record is the culprit of this error?
It would be desperately complicated to determine it, in case you do many operations on LINQ objects before .Submit()
Is there any way to determine what certainly caused this error?
This error typically happens when you are creating a new record in a MetaTable with a foreign key relationship and the foreign key record already exists.
For example, let's say you have an Contact table and an Address table, and each Contact can hold multiple Addresses. The error occurs when you create a new Contact record and try to manually associate an existing Address record to that new Contact.
Assuming that the passed Address ID represents an existing Address record, this doesn't work:
public class Contact
{
public int Contact_ID { get; set; }
public string Name { get; set; }
public Address ContactAddress { get; set; }
public string Phone { get; set; }
}
public class Address
{
public int Address_ID { get; set; }
public string Street { get; set; }
public string CityState { get; set; }
public string ZIP { get; set; }
}
public void CreateNewContact(int addressID)
{
Contact contact = new Contact();
contact.Name = "Joe Blough";
contact.ContactAddress.Address_ID = addressID;
contact.Phone = "(555) 123-4567";
DataContact.SubmitChanges();
}
Historically, SQL developers are trained to just pass the ID value in order for the magic to happen. With LINQ-to-SQL, because the database activity is abstracted, we have to pass the whole object so that the LINQ engine can properly reflect the necessary changes in the ChangeSet. In the above example, the LINQ engine assumes that you are asking to create a new Address record, because it didn't have one to work with when the SubmitChanges was made and it has to respect the contract established by the foreign key relationship. It creates a blank Address record with the passed ID value. The error occurs because that ID value already exists in the data table and the ChangeSet has not flagged the Address delta as an Update.
The fix is to pass in the entire record, not just the ID value:
contact.ContactAddress = DataContext.Addresses.Where(a => a.Address_ID == addressID).Single();
Now, the LINQ engine can properly flag the incoming Address record as an existing one and not try to recreate it.
May be the column you trying to Attach(), Remove(), Add() or DeleteOnSubmit(), is a primary key and you are trying to add or attach the same value again.
Also you might be accessing a primary key or foreign key value column in a different method and it's not closed yet when you trying to call the above methods.
Above to these methods Attach(), Remove(), Add() or DeleteOnSubmit(), try to create a new instance of your datacontext again and run.
It sounds like you are doing an Table.Attach() and the entity you are attaching has a key value that L2S is already tracking. This has got nothing to do with a duplicate key in your physical database.
As explained on one of the answers above, this error is more likely due to trying to insert a record into the table with a repeated value on primary ID key field. You could solve the problem by selecting/creating a different primary key.