EF6 Code First Mapping where key name is different in DB - c#

I am trying to do a "code first" EF model based off an existing db (which I recognize immediately is counter to the concept of an EF code-first approach), but I keep running into a problem, and the error message does not make sense to me, so I'm hoping for some insight.
My table is defined like so:
CREATE TABLE [dbo].[BackupLocales]
(
[Id] UNIQUEIDENTIFIER NOT NULL PRIMARY KEY,
[LocaleId] INT NOT NULL constraint fkBackupLocales_Locale references dbo.Locales(Id),
[BackupLocaleId] INT NOT NULL constraint fxBackupLocales_BackupLocale references dbo.Locales(Id),
[Weight] INT NOT NULL DEFAULT 1
)
go
create unique nonclustered index uxBackupLocales_Locale_Weight on dbo.BackupLocales(LocaleId, [Weight]);
In code, I wrote the following entity class:
public class BackupLocale
{
[Key]
[Column("Id")]
public Guid Id { get; set; }
public int Weight { get; set; }
[Column("LocaleId")]
public int PrimaryId { get; set; }
[ForeignKey("PrimaryId")]
public virtual Locale Primary { get; set; }
[Column("BackupLocaleId")]
public int BackupId { get; set; }
[ForeignKey("BackupId")]
public virtual Locale Backup { get; set; }
}
... which, as far as I can tell (based off of the samples and documentation I've read), look correct. The only "weirdness", as far as I can see, is the fact that the column name for the Primary locale is different in the database; I wanted to make it more specific in code than it is in the table.
Naturally, this does not work when trying to load the entity model (or else why would I be asking a question? :) )
I get a rather long error message, but the heart of it would be this part:
System.Data.Entity.Core.EntityCommandExecutionException: An error
occurred while executing the command definition. See the inner
exception for details. ---> System.Data.SqlClient.SqlException: Invalid
column name 'LocaleId'.
The problem here is, if I were not using EF, a SqlClient.SqlException with the message "Invalid column name 'LocaleId'" would mean that the column literally did not exist in the SQL server... but that is definitely not the case here. It does exist... so I'm not sure what's going on here. The only thing I can figure is the fact that I am changing the name of the property is somehow screwing up the mapping, but of course it could be that I'm just doing something dumb.
Have I misunderstood how the attributes are supposed to work? Have I configured this incorrectly? Or am I trying to do something that is not supported in EF6?

It looks like this was a Visual Studio problem. I closed the solution, exited VS, restarted VS, and loaded the project, and it started working. :(

Related

SQLite seems to have a problem with [DatabaseGenerated(DatabaseGeneratedOption.Identity)]

I have a problem with the SQLite in-memory database. The normal database is working.
This is my model code
public class Log
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Message { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime CreatedAt { get; set; }
}
The SQL statement to create the table
CREATE TABLE [dbo].[AuditLogs]
(
[Id] UNIQUEIDENTIFIER NOT NULL DEFAULT newid() PRIMARY KEY,
[Message] varchar(max) NOT NULL CONSTRAINT ensure_json CHECK (ISJSON([Message])> 0),
[CreatedAt] datetime NOT NULL default GetDate()
)
The error
Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 19: 'NOT NULL constraint failed: AuditLogs.CreatedAt'.
Do you have any solution?
You could just use this:
public Guid Id { get; set; } = Guid.NewGuid();
The problem with the Entity Framework is that it won't generate keys for you. If it is database-generated then some trigger in the database would still need to create this ID. This is generally done when the ID is of type int, but not Guid. Then again, SQLite is a weird database provider to begin with as it doesn't really has datatypes. Data type definitions are more suggestions and not enforced by the engine. (But EF will enforce it.)
Anyways, since you use Guids there's nothing wrong with assigning new values to the property, as they will be overwritten by the value in the database on retrieval. But SQLite isn't really generating values for you.
Also, I would use public DateTime CreatedAt { get; set; } = DateTime.Now(); for the same reason. I myself actually had similar problems but I use the Fluid API instead and use this:
var hostBuilder = modelBuilder.Entity<Host>();
hostBuilder
.Property(r => r.Id)
.HasColumnOrder(0)
.IsRequired()
.HasColumnName("Key")
.HasColumnType("varchar(36)")
.HasComment($"Primary key");
hostBuilder
.Property<DateTime>("Created")
.HasColumnOrder(1)
.HasDefaultValueSql("CURRENT_TIMESTAMP")
.ValueGeneratedOnAdd()
.HasComment($"When was it created?");
hostBuilder
.HasKey(r => r.Id)
.HasName($"PK_Visitor_Host_Key");
And my class only has the Id property defined, as I don't need the Created field in my project. It still gets added, though! The HasDefaultValueSql() call will tell that the field is database-generated, including how it's generated. You might want to look into this Fluid API for your project. I prefer it over those attributes as it provides more options and better control, plus I can add fields to tables that are not important for my code, yet still required for other purposes...
(Btw. You don't want timestamps to be unique as two records could be created at exactly the same timestamp on fast systems.)

Entity Framework creates non-existing column in query

I face the problem that EF creates a column in the query that does not exist in the Oracle database table.
The simplified model which is created by EF looks like this (I use DB first approach):
public partial class USER
{
public int ID { get; set; }
public string NAME { get; set; }
public int PROCESS_ID { get; set; }
public virtual PROCESS PROCESS { get; set; }
}
public partial class PROCESS
{
public PROCESS()
{
this.USER = new HashSet<User>();
}
public int ID { get; set; }
public virtual ICollection<USER> USER { get; set; }
}
I set up the foreign key constraint in the oracle sql developer.
When I try to get the Users for a selected Process like this:
var users = context.Users.Where(u => u.PROCESS_ID == 0);
It produces following error:
ORA-00904: "Extent1"."R1": invalid ID
So i took a look on the produced SQL:
SELECT
"Extent1".ID,
"Extent1".NAME,
"Extent1".R1,
FROM DB.USER "Extent1"
WHERE "Extent1".R1 = :p__linq__0
Of course this produces an error because R1 isn't a column in the table. But I can't figure out where it comes from. It seems like EF can't map the foreign key properly thats why it's also missing in the generated SQL query?
Maybe someone has a tip for me :)
To follow up my comment, here is a link to the conventions.
The convention for a foreign key is that it must have the same data type as the principal entity's primary key property and the name must follow one of these patterns:
[navigation property name][principal primary key property name]Id
[principal class name][primary key property name]Id
[principal primary key property name]Id
Your convention [navigation property name]_ID isn't on the list.
Encountered the same error recently while working with Oracle using DevArt provider. Turned out it was caused by a column name being longer than 30 chars. OP mentioned that the model posted in his question is a simplified one so it still may be the case.

EF 6 calls INSERT instead of UPDATE

This could be a duplicate question but a lot of searching for the words in the title only got me a lot of unrelated results.
I have an entity that's roughly set up like this:
public abstract class A
{
public GUID AId { get; set; }
public string SomeProperty { get; set; }
}
public class B : A
{
public string SomeOtherProperty { get; set; }
}
The context has public DbSet<B> BInstances { get; set; } for B objects. In OnModelCreating, the mapping has A set to ignored and B is mapped to a table called TableB.
The AId field is not auto-generated (not an identity field) but it's set to be primary key, both in the database and in the mapping. In the database, the field is defined as a non-null uniqueidentifier with no default.
At runtime, I'm loading an instance of B using its key (_token is just a CancellationToken):
var b = await (dbCtx.BInstances.FirstOrDefaultAsync(e => e.AId), _token));
Then, a property of b is set and I try to save it back to database:
b.SomeOtherProperty = "some new text";
await (dbCtx.SaveChangesAsync(_token));
At this point, I'm getting a Violation of PRIMARY KEY constraint error from the database, stating that the value of AId cannot be inserted because it'd be a duplicate. Of course, the ID is already in the database, I loaded the entity from there, using the ID. For some reason, EF generates an INSERT statement, not an UPDATE and I don't understand why.
When I check dbCtx.Entry(b).State, it's already set to EntityState.Modified. I'm at a loss - can someone point out what I'm doing wrong? I never had issues with updating entities before but I haven't used EF with GUID primary keys (usually I use long primary keys).
I'm using EF 6 and .NET Framework 4.7.1.
Thank you all for the suggestions - this turned out to be a mapping problem that I caused.
In my OnModelCreating() call, I called MapInheritedProperties() on a type that didn't inherit from a base class (other than object, of course) - this seems to have triggered a problem. Other entities that do share a base class worked fine with the mapping call.
I also called ToTable() directly against the entity class - this broke my table mapping for reasons I do not understand. Once I moved that call inside Map(), it started working as expected.
So I went from this:
entity.ToTable("tablename");
to this:
entity.Map(m => m.ToTable("tablename"));
to solve the problem.
Hopefully this will be useful for future readers.
try this
b.SomeOtherProperty = "some new text";
dbCtx.BInstances.AddOrUpdate(b);
await (dbCtx.SaveChangesAsync(_token));
AddorUpdate will update your b instance if it is already added.

Cannot insert the value NULL into column in ASP.NET MVC Entity Framework

When trying to use this code:
var model = new MasterEntities();
var customer = new Customers();
customer.Sessionid = 25641;
model.Customers.Add(customer);
model.SaveChanges();
I get:
{"Cannot insert the value NULL into column 'Sessionid', table
'master.dbo.Column'; column does not allow nulls. INSERT
fails.\r\nThe statement has been terminated."}
The column "Sessionid" is actually the primary key and is marked with [KEY] like this:
public class Customers
{
[Key]
public long Sessionid { get; set; }
public long? Pers { get; set; }
}
So according to this question, it seems as if when the property is marked with [KEY], EF ignores my own declaration of Sessionid since it expects the database to assign the value.
So how can I solve this? If I remove [KEY] I get the "entity type has no key defined" exception...
I solved it by adding [DatabaseGenerated(DatabaseGeneratedOption.None)] like this:
public class Customers
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Sessionid { get; set; }
public long? Pers { get; set; }
}
You can configure SQL to auto-generate (and auto-increment) the primary key for the table upon inserts. Then just remove the [Key] in C# and you don't need to set the ID in the application manually, the db will generate it for you.
I have encountered this problem multiple times while working with Microsoft SQL Server and I have followed the same way to fix it. To solve this problem, make sure Identity Specification is set to Yes. Here's how it looks like:
In this way the column number auto increments as a primary key normally would.
HOW?: right-click the table that contains the column, choose Design, select the primary key and in Column Properties window find Identity Specification and set it to Yes.

Entity Framework 4.1. Updating an entity. What does this error message mean?

I am trying to update an entity. The code is partially working however, it seems to fail for particular values of shopItem.GlaId
Gla entities do exist with the ids that fail though.
I cannot see any difference between working values and non-working. The lazy loading works for other properties but not for the Gla property in question.
The error I'm getting is:
The UPDATE statement conflicted with the FOREIGN KEY constraint "OrderItem_Gla". The conflict occurred in database "PPPContext", table "dbo.Pap", column 'Id'.
(nb shopItem extends orderItem).
I am having trouble understanding the meaning of this error message.
public ActionResult UpdateShopItem(ShopItem shopItem)
{
var u = db.GetUser();
var dbShopItem = db.ShopItems.Find(shopItem.Id)
TryUpdateModel(dbShopItem);
db.SaveChanges();
It seems that table "dbo.Pap" has reference to "ShopItems" table.
And it seems that shopItem.Id that you pass
inside public ActionResultUpdateShopItem(ShopItem shopItem)` is null or empty.
Debug here
var u = db.GetUser();
and check shopItem
SOLUTION:
Make sure that shopItem has correct Id or make ShopItem as nullable object
ShopItem? shopItem
Nullable<ShopItem> shopItem
A kind of silly error, caused by a cut and paste typo.
in shopItem:
public virtual Pap Gla { get; set; }
it should have been:
public virtual Gla Gla { get; set; }
In other words I had the type of one of my properties wrong. Well I hope it helps someone else!

Categories

Resources