Entity Framework not detecting an existing table - c#

i am facing some troubles with entity framework.
It seems that my code detects a table that don't actually exist in the database (Code First Generation).
Well i mentionned in my identity model code this line :
public virtual List<Message> Messages { get; set; }
in the fluent API part i did :
modelBuilder.Entity<Message>().ToTable("Messages");
modelBuilder.Entity<ApplicationUser>()
.HasMany(c => c.Messages)
.WithRequired(c => c.SendedByUser)
.WillCascadeOnDelete(true);
Just above the Message class :
[Table("Messages")]
Still when i add a migration, the result is as below :
And as you can see, the migration try to use some Messages table that it didn't create yet, left side is the server explorer, where you can see the actual tables that i really have.
How can i fixe that so that EF updates my database correctly by creating automatically the Messages table without messing with it manually?

Related

EF Core - How to allow multiple cascade paths for foreign key deletion

I have a database in SQL Server 2019, with 4 tables. 1 for users, 1 for contacts, 1 for messages, and the last table that contains joined relationships between contacts and messages. (See image)
Contacts and Messages are linked to the user via a foreign key, so when the user gets deleted, it would cascade the deletion of contacts and messages.
Messages can have multiple contacts linked, but not all contacts will be linked to a message.
A message may have no contacts at all.
I now need a foreign key that would delete the relevant records in the MessageContacts table when a contact gets deleted, as well as when a message gets deleted.
(Please note that this is just a demo scenario I set up to explain my problem)
Creating this scenario in Entity Framework Core, does not work, and gives me the following error:
Introducing FOREIGN KEY constraint 'FK_MessageContacts_Messages_messageId' on table 'MessageContacts' may cause cycles or multiple cascade paths
Please see the configuration of my DbContext below:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<AppUser>(entity =>
{
entity.ToTable("AppUser");
entity.HasKey(e => e.id);
});
modelBuilder.Entity<Message>(entity =>
{
entity.ToTable("Messages");
entity.HasKey(e => e.id);
entity.HasOne<AppUser>().WithMany().HasForeignKey(e => e.userId);
});
modelBuilder.Entity<Contact>(entity =>
{
entity.ToTable("Contacts");
entity.HasKey(e => e.id);
entity.HasOne<AppUser>().WithMany().HasForeignKey(e => e.userId);
});
modelBuilder.Entity<MessageContact>(entity =>
{
entity.ToTable("MessageContacts");
entity.HasKey(e =>new {e.messageId,e.contactId });
//Removing one of the below prevents my error, but then cascading deletions don't work
entity.HasOne<Message>().WithMany().HasForeignKey(e => e.messageId);
entity.HasOne<Contact>().WithMany().HasForeignKey(e => e.contactId);
});
}
Removing the FK from MessageContacts for either Contacts or Messages prevents the error, but then the delete cascading does not work correctly. IE. Removing the FK on MessageContacts to Messages means that deleting a message, will not delete related records in MessageContacts leaving me with orphans.
Note: I have implicitly set the .OnDelete(DeleteBehavior.Cascade) option and it did not solve my problem either.
So I guess my question is, how do I accomplish this without the ability to define multiple cascade paths?
I would like to avoid doing anything manually in the database, so have everything done via EF Core migrations (if possible).
Maybe the entire model above is flawed, and someone has a better solution?
Thanks in advance.
The issue is with Microsoft's SQL Server and there's no way to get around it, unless you're willing to forget about cascading deletions via foreign keys, and instead, record the Id of the record you want to delete, then use it to manually remove all related records. That way, you aren't left with a bunch of orphaned records.
This is a really terrible way to go about it. The worst part is that SQL Server is a paid-for RDBMS, but it doesn't allow you to do something, that you can do using a free RDBMS like MySql or MariaDB.
So my solution?
Goodbye Sql Server, hello MySql.
This solution is only as practical as a developer or administrator's ability to switch from one RDBMS to another. This may not be possible at all in certain production scenarios.
I wish I could provide a better answer, but I don't think there is one.

Entity Framework Core and SQL Server 2016 temporal tables

We are using EF Core and SQL Server 2016 for our .NET Core Web API. I am evaluating use of temporal tables and its impact on EF Core code. When I generate the EF model using cmdline then it generates model with appstart, append and mappings in dbcontext. When I do insert/update they fail saying these columns cannot be updated. I had to remove appstart, end from model and dbcontext mapping to make it work. I read there is no interception capability yet in EF Core like EF 6.x.
Please advise about any better solutions for this.
I tried below options and they are working.
option 1: removed appstart, append fields from entities and
dbcontext mappings and updates/insert started working properly.
option 2: decorate period columns with
attributes as below.
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime StartTime { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime EndTime { get; set; }
There currently is no better solution for this, and the feature is on the backlog.
Making the Period start column(StartTime) and Period end column(EndTime) hidden should fix this issue. We can do this by
ALTER TABLE [dbo].[Table1] ALTER COLUMN [StartTime] ADD HIDDEN;
ALTER TABLE [dbo].[Table1] ALTER COLUMN [EndTime] ADD HIDDEN;
We can see the settings for hidden against these columns in the sys.columns table
SELECT * FROM sys.columns WHERE is_hidden = 1
I think there's a better solution for this as follows:
Create partial context file (to prevent re-making the changes after re-generating the model) as follows
public partial class DatabaseDBContext : DbContext
{
partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Table1>(entity =>
{
entity.Property(e => e.StartTime)
.ValueGeneratedOnAddOrUpdate();
entity.Property(e => e.EndTime)
.ValueGeneratedOnAddOrUpdate();
});
}
}
.NET 6 and Entity Framework Core 6.0 supports SQL Server temporal tables out of the box.
See this answer for examples:
https://stackoverflow.com/a/70017768/3850405

Entity Framework, Fluent API mapping, should it be done somewhere other than OnModelCreating?

The Question:
My question is if I'm going about this correctly, or whether there's a tidier/more-robust way of mapping entities with Fluent API? Mainly, should I be doing all the mapping within the overidden OnModelCreating method? Secondly, is there any reason not to do it this way? And finally, if my datamodel is working, and I'm able to successfully access the database, why would Entity Framework Power Tools not be able to generate a read-only .edmx file?
Here's the back-story and examples of what I'm doing:
So I have a working code-first data model of ~70 very interconnected entities. I reverse engineered it from an existing database that had been using and Entity-Framework designer to relate objects and map them to the SQL db.
I modeled my approach on the tutorials I've done, and I'm using Fluent API for mapping, not Data Annotations.
So I have all the entity models defined as POCOs, each in their own source file. I have another class inheriting DbContext, we'll call this MyDb. Inside MyDb I have the ~70 DbSet properties
public virtual DbSet<SomeEntity> SomeEntities{ get; set; }
public virtual DbSet<OtherEntity> OtherEntities{ get; set; }
//...You get the idea, there another few dozen of these below
public virtual DbSet<LastEntity> LastEntities{ get; set; }
//Well that was tedious
Following the DbSets that provide access to all the entities, I have ~6000 lines of fluent API mappings inside protected override void OnModelCreating(DbModelBuilder mb) , which is how I learned to do it in the examples, and in the smaller test projects I've done.
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<SomeEntity>()
.ToTable("SomeEntities")
.HasKey(se => se.ID);
mb.Entity<SomeEntity>()
.Property(se => se.ID)
.HasColumnName("ID")
.HasColumnType("bigint")
.IsRequired()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
mb.Entity<SomeEntity>()
.Property(se => se.Contents)
.HasColumnName("Content")
.HasColumnType("varchar")
.IsMaxLength()
.IsUnicode(true)
.IsRequired()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
mb.Entity<SomeEntity>()
.HasMany(se => se.OtherEntities)
.WithRequired(oe => oe.SomeEntity)
.HasForeignKey(oe => oe.SomeEntity_ID);
//and so on, there are another ~70 of these that go on for the
//next 6000 lines
}
Obviously all my entities aren't this simple, and there are many that are unconventional in their property naming, table naming, table types, and so on; that's why I've been quite verbose with the Fluent API.
#Gert Arnold did a good job of answering your first questoins. Regarding the EF Power Tools, they're probably just getting hung up while trying to discover your model. You can do the same thing manually using the following code snippet.
using (var db = new MyContext())
using (var writer = XmlWriter.Create("MyContext.edmx"))
{
EdmxWriter.WriteEdmx(db, writer);
}
I think in terms of performance there's not a markable difference whether you do the configuration by separate EntityTypeConfiguration classes or in OnModelCreating only.
But computer languages were not invented to help computers, but us, binary dyslectic types. Therefore, I would prefer EntityTypeConfigurations, because that helps you to get to a specific mapping quicker than scrolling through 6000 lines of code. We developers spend a lot of time referring back to what we or others wrote before.
I think the issue about EF Power Tools not generating a read-only .edmx file should be a different question. It requires more details on that specific problem.

Entity Framework issues - appends a "1" to my table name?

I have the following model-first (is that what it's called?) diagram that I have made. I use T4 to generate the classes.
Now, I have a problem that causes Entity Framework to somehow append a "1" to the table name of the DatabaseSupporter entity. The database has been generated from this very model, and nothing has been modified.
I am trying to execute the following line:
_entities.DatabaseSupporters.SingleOrDefault(s => s.Id == myId);
The error I receive when executing that line (along with its inner exception below) is:
An exception of type
'System.Data.Entity.Core.EntityCommandExecutionException' occurred in
mscorlib.dll but was not handled in user code.
Invalid object name 'dbo.DatabaseSupporter1'.
I tried fixing the problem with the following Fluent API code (notice the second line in the function that names the table explicitly to "DatabaseSupporter"), but with no luck.
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
modelBuilder
.Entity<DatabaseSupporter>()
.HasOptional(f => f.DatabaseChatSession)
.WithOptionalPrincipal(s => s.DatabaseSupporter);
modelBuilder
.Entity<DatabaseSupporter>()
.Map(m =>
{
m.Property(s => s.Id)
.HasColumnName("Id");
m.ToTable("DatabaseSupporter");
});
modelBuilder
.Entity<DatabaseSupporter>()
.HasMany(s => s.DatabaseGroups)
.WithMany(g => g.DatabaseSupporters)
.Map(m =>
{
m.ToTable("DatabaseSupporterDatabaseGroup");
m.MapLeftKey("DatabaseGroups_Id");
m.MapRightKey("DatabaseSupporters_Id");
});
modelBuilder
.Entity<DatabaseGroup>()
.HasRequired(g => g.DatabaseChatProgram)
.WithMany(c => c.DatabaseGroups);
modelBuilder
.Entity<DatabaseGroup>()
.HasRequired(g => g.DatabaseOwner)
.WithMany(o => o.DatabaseGroups);
modelBuilder
.Entity<DatabaseOwner>()
.HasMany(o => o.DatabaseChatSessions)
.WithRequired(o => o.DatabaseOwner);
base.OnModelCreating(modelBuilder);
}
It should be mentioned that the Id property for every entity actually is a Guid.
I am using Entity Framework 6.0.2.
Any ideas?
Edit 1
Here's the generated DatabaseSupporter.cs file containing my DatabaseSupporter entity as requested in the comments.
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Coengage.Data.Entities
{
using System;
using System.Collections.Generic;
public partial class DatabaseSupporter
{
public DatabaseSupporter()
{
this.DatabaseGroups = new HashSet<DatabaseGroup>();
}
public bool IsActive { get; set; }
public string Username { get; set; }
public System.Guid Id { get; set; }
public virtual DatabaseChatSession DatabaseChatSession { get; set; }
public virtual ICollection<DatabaseGroup> DatabaseGroups { get; set; }
}
}
Edit 2
The errors started occuring after I added the many-to-many link between DatabaseSupporter and DatabaseGroup. Before that link, the Fluent code wasn't needed either.
This mapping is incorrect:
modelBuilder
.Entity<DatabaseSupporter>()
.Map(m =>
{
m.Property(s => s.Id)
.HasColumnName("Id");
m.ToTable("DatabaseSupporter");
});
It is kind of 50 percent of a mapping for Entity Splitting - a mapping that stores properties of a single entity in two (or even more) separate tables that are linked by one-to-one relationships in the database. Because the mapping is not complete you even don't get a correct mapping for Entity Splitting. Especially EF seems to assume that the second table that contains the other properties (that are not explicitly configured in the mapping fragment) should have the name DatabaseSupporter1. I could reproduce that with EF 6 (which by the way has added a Property method to configure single properties in a mapping fragment. In earlier versions that method didn't exist (only the Properties method).) Also the one-to-one constraints are not created correctly in the database. In my opinion EF should throw an exception about an incorrect mapping here rather than silently mapping the model to nonsense without exception.
Anyway, you probably don't want to split your entity properties over multiple tables but map it to a single table. You must then replace the code block above by:
modelBuilder.Entity<DatabaseSupporter>()
.Property(s => s.Id)
.HasColumnName("Id");
modelBuilder.Entity<DatabaseSupporter>()
.ToTable("DatabaseSupporter");
The first mapping seems redundant because the property Id will be mapped by default to a column with the same name. The second mapping is possibly also redundant (depending on if table name pluralization is turned on or not). You can try it without this mapping. In any case you shouldn't get an exception anymore that complains about a missing dbo.DatabaseSupporter1.
I have replicated your model exactly as you have listed it and I cannot currently reproduce your issue in the DDL that the EDMX surface emits when Generating Database from Model.
Could you please provide detailed information on exactly how you are going about adding your many-to-many relationship between DatabaseGroup and DatabaseSupporter? You say that you're trying to add the relationship on the edmx surface and NOT through code and it craps on your table name?
I added this thing Many-to-many from DatabaseGroup to DatabaseSupporter
I added this thing Many-to-many from DatabaseSupporter to DatabaseGroup
Can you please provide the following:
Rollback to your codebase prior to adding the many-to-many relationship. Ensure that your EF Fluent API code is not currently in your project.
Generate the DDL from this surface and confirm that it is not being
generated with the name DatabaseSupporters1 (Post the tablename that
it chooses at this stage. DatabaseSupporter or DatabaseSupporters)
Now, right click DatabaseGroup| Add New| Association
Choose DatabaseGroup for the left and DatabaseSupporter for the
right. Confirm that the name of the association that the designer
chooses is DatabaseGroupDatabaseSupporter [Do not create]
Choose DatabaseSupporter for the left and DatabaseGroup for the
right. Confirm that the name of the association that the designer
chooses is DatabaseSupporterDatabaseGroup [Create]
From the edmx surface, right click the many-to-many association just created and click "Show in Model Browser"
Edit your post to include the settings that display.
Also, right click the surface and click "Generate Database from Model."
Edit your post to include the DDL that gets generated. The table
should be named [DatabaseSupporters]
(My first inclination is that it's going to have something to do with your navigation properties, but not entirely sure. I actually had Entity Framework do the same thing to me in a toy project I was working on but I recall it being trivial to correct and I don't recall what the root cause was; I seem to recall it being something about the nav properties)
[Edit]
Wait.....
If I remove the many-to-many that doesn't fix my problem. However,
reverting to before I added the many-to-many fixes it. The exact code
that throws the exception is already shown. If I remove my fluent
mappings entirely, it's not the same exception being thrown (it throws
something about a group and a supporter, and a principal). I have not
tried recreating the model in an empty project - that takes a lot of
time. I already tried searching the EDMX in Notepad for references -
none were found.
(note my added emphasis)
So the DatabaseSupporter1 error showed up after you tried your fluent api patch? Get rid of the patch, add the many-to-many and give us the real error then.
...also, it took me 5 minutes to build this diagram. I wouldn't qualify that as "a lot of time."
I don't have my dev environment here in front of me, but my immediate thoughts are:
FIRST
Your fluent looks ok - but is the plural s in your ID column correct? And no plural (s) on the table names? This would be the opposite of convention.
SECOND
EF will automatically append a number to address a name collision. See similar question here: Why does EntityFramework append a 1 by default in edmx after the database entities?
Any chance you have something hanging around - a code file removed from your solution but still in your build path? Have you tried searching your source folder using windows explorer rather than the visual studio?
modelBuilder
.Entity<DatabaseSupporter>()
.HasMany(s => s.DatabaseGroups)
.WithMany(g => g.DatabaseSupporters)
.Map(m =>
{
m.ToTable("DatabaseSupporterDatabaseGroup");
m.MapLeftKey("DatabaseGroups_Id");
m.MapRightKey("DatabaseSupporters_Id");
});
Left and Right are inversed on Many to Many.
Try this :
modelBuilder
.Entity<DatabaseSupporter>()
.HasMany(s => s.DatabaseGroups)
.WithMany(g => g.DatabaseSupporters)
.Map(m =>
{
m.ToTable("DatabaseSupporterDatabaseGroup");
m.MapLeftKey("DatabaseSupporters_Id");
m.MapRightKey("DatabaseGroups_Id");
});
I think the DatabaseSupporter class created two time
one name is : DatabaseSupporter
another one is : DatabaseSupporter1
The modified changes are stored in DatabaseSupporter1 and mapping to here.
You need to copy the DatabaseSupporter1 class code and past the code to DatabaseSupporter class . then delete this DatabaseSupporter1 class.
I had this issue from renaming tables in the diagram, specifically changing just the capitalization.
If you rename a table by clicking on the header in the diagram, I think it checks the entity set name before trying to change it, sees it exists (even though it's the same entity set), and appends a 1.
However, if you right-click and open the Properties pane and first rename the Entity Set Name, then change the Name second, it won't add the number.
In my case i have two tables in the same database with the same name (2 different schemas(see image)

Fluent NHibernate Many-to-many mapping with auto-generated pk instead of composite key

I'm working on a RoleProvider in .NET, using Fluent NHibernate to map tables in an Oracle 9.2 database.
The problem is that the many-to-many table connecting users and roles uses a primary key generated from a sequence, as opposed to a composite key. I can't really change this, because I'm writing it to be implemented in a larger existing system.
Here is my UserMap:
public UserMap()
{
this.Table("USR");
HasMany(x => x.Memberships).Cascade.All()
.Table("MEMBERSHIP").Inverse().LazyLoad();
HasManyToMany(x => x.Roles)
.Table("USR_ROLE")
.Cascade.SaveUpdate()
.ParentKeyColumn("USR_ID")
.ChildKeyColumn("ROLE_ID")
.Not.LazyLoad();
}
And my RoleMap:
public RoleMap()
{
this.Table("ROLE");
Map(x => x.Description).Column("ROLE_NAME");
Map(x => x.Comment).Column("ROLE_COMMENT");
HasManyToMany(x => x.Users)
.Table("USR_ROLE")
.ParentKeyColumn("ROLE_ID")
.ChildKeyColumn("USR_ID")
.Inverse();
}
Yet, this is giving me the error:
Type 'FluentNHibernate.Cfg.FluentConfigurationException' in assembly 'FluentNHibernate, Version=1.0.0.593, Culture=neutral, PublicKeyToken=8aa435e3cb308880' is not marked as serializable.
Is there a simple fix to allow this HasMayToMany to use my PersistentObjectMap extension? I'm thinking I may have to add a convention for this many-to-many relationship, but I don't know where to start with that, since I've just started using NHibernate and Fluent NHibernate only recently.
I've been working on this problem for a while and I can't seem to find a solution.
Any help would be much appreciated. Thanks.
EDIT: I think I've found a possible solution here: http://marekblotny.blogspot.com/2009/02/fluent-nhbernate-and-collections.html
I'll try the above method of creating an entity and a class map for the linking table and post my findings.
EDIT 2: I created a linking entity as mentioned in the above blog post and downloaded the newest binaries (1.0.0.623).
This helped me discover that the issue was with setting lazy load and trying to add roles to the user object in a completely new session.
I modified the code to move OpenSession to the BeginRequest of an HttpModule as described here. After doing this, I changed my data access code from wrapping the open session in a using statement, which closes the session when it is finished, to getting the current session and wrapping only the transaction in a using statement.
This seems to have resolved the bulk of my issue, but I am now getting an error that says "Could not insert collection" into the USR_ROLE table. And I'm wondering if the above code should work with a UserRoleMap described as:
public UserRoleMap()
{
this.Table("USR_ROLE");
/* maps audit fields id, created date/user, updated date/user */
this.PersistentObjectMap("USR_ROLE");
/* Link these tables */
References(x => x.Role).Column("ROLE_ID");
References(x => x.User).Column("USR_ID");
}
Hibernate's documentation for many-to-many relationship suggests creating an object to maintain a one-to-many/many-to-one, as in an ERD. I'm sure this would be much easier with conventional naming standards, but I have to stick with certain abbreviations and odd (and not always properly-implemented) conventions.
To fix this, I created an Entity, Mapping, and Repository for UserRole. And, instead of HasManyToMany mapping in the User and Role Entities, I have a HasMany mapping. It's a little weird, because I now have:
IList<UserRole> UserRoles {get; protected set;}
and IList<Role> Roles { get{ return UserRoles.Select(u => u.Role).ToList(); } }
This works, however, I'm not 100% sure why this works and the HasManyToMany doesn't.

Categories

Resources