Entity framework remove a relationship with migrations - entity-framework

I am using Entity Framework 5 with migrations. My model looks like this:
UserRegistration
Sessions
For each registration there can be many sessions. I was having a problem with a SQL cascading delete error when trying to run migrations, so I attempted to remove the cascade delete by adding the following code:
modelBuilder.Entity<UserRegistration>()
.HasOptional(x => x.Sessions)
.WithMany()
.WillCascadeOnDelete(false);
Now if you know EF you can see an error with that code. I thought it was giving me a 1 to many relationship from UserRegistrations to Sessions, when in fact this code says "Each UserRegistration has an optional Session, each Session has many UserRegistrations". So my 1 to many relationship was going the wrong way. I believe this should have been the code I added.
modelBuilder.Entity<UserRegistration>()
.HasMany(x => x.Sessions)
.WithRequired(x => x.UserRegistration)
.WillCascadeOnDelete(false);
However, when I remove the bad code (first block above), and run add-migration, I get this:
public override void Up()
{
DropForeignKey("dbo.UserRegistrations", "Sessions_Id", "dbo.UserRegistrationSessions");
DropIndex("dbo.UserRegistrations", new[] { "Sessions_Id" });
RenameColumn(table: "dbo.UserRegistrationSessions", name: "Sessions_Id", newName: "UserRegistration_Id");
}
The first two lines (DropForeignKey and DropIndex) looks good, this is removing the foreign key from the UserRegistrations table. The 3rd line, however, comes out of nowhere. There is no Sessions_Id column in the UserRegistrationSessions table. When I run it, I expectedly get an error:
Either the parameter #objname is ambiguous or the claimed #objtype (COLUMN) is wrong.
I'm thinking something is out of whack between my model and my modelbuilder code. Maybe I need to try to make my model look incorrect, but match the modelbuilder code, before moving forward with the fix.
Here is the relevant code in my models:
public class UserRegistration
{
public int Id { get; set; }
public virtual ICollection<UserRegistrationSession> Sessions { get; set; }
}
public class UserRegistrationSession
{
public int Id { get; set; }
public virtual UserRegistration UserRegistration { get; set; }
}

This is more of an answer to the question in the comment, which is 'where is Entity Framework getting its information from'. The answer is probably the serialized version of the model in the resource file attached to the migration.
If you haven't yet deployed the migrations out in the wild, is it possible to remove the offending migration and roll back the DB to before the migration was created?

Related

How to make Many-to-one mapping without foreign key in EF Core?

Using EF Core 5.0, I have a PK-less entity (from a SQL view) OrderInfo, which has a column OrderDetailId. I also have an entity DiscountOrder which a PK from the columns OrderDetailId and DiscountId.
I would like to create a navigation property from Order to DiscountOrders. Such as:
public class OrderInfo
{
public int OrderDetailId { get; set; }
public virtual ICollection<DiscountOrder> DiscountOrders { get; set; }
}
public class DiscountOrder
{
public int DiscountId { get; set; }
public int OrderDetailId { get; set; }
}
// For reference, this entity also exists
public class Discount
{
public int DiscountId { get; set; }
}
Obviously, there are no FKs to make use of, but I should be able to create a navigation property anyway.
I think I should be able to do this:
modelBuilder.Entity<OrderInfo>(e =>
{
e.HasNoKey();
e.HasMany(x => x.DiscountOrders)
.WithOne()
.HasPrincipalKey(o => o.OrderDetailId)
.HasForeignKey(pb => pb.OrderDetailId)
.IsRequired(false);
});
But a query on DbSet<OrderInfo> results in a NullReferenceException with the breakpoint landing on the HasMany() line. That said, I don't do anything with the DiscountOrders property, so the exception seems like it would have to be configuration related.
I've looked at answers to similar questions, but most answers use HasOne().WithMany() where as I'd like to keep this definition on OrderInfo since we don't really care about the other direction. How can I correctly set up this mapping?
Keyless entities (entity without key) cannot be principal of a relationship, because there is no key to be referenced by the FK property of the dependent.
Note that by EF Core terminology key is primary key. There are also alternate (unique) keys, but EF Core does not enable them for keyless types.
So basically HasNoKey() disables alternate keys and relationships to that entity. Just the exception is unhandled, hence not user friendly. For instance, if you try to predefine the alternate key referenced by .HasPrincipalKey(o => o.OrderDetailId) in advance
e.HasNoKey();
e.HasAlternateKey(o => o.OrderDetailId);
you'll get much better exception message at the second line
The key {'OrderDetailId'} cannot be added to keyless type 'OrderInfo'.
Shortly, e.HasNoKey(); and `.HasPrincipalKey(o => o.OrderDetailId); are mutually exclusive.
The only way to make it work is to define PK for OrderInfo even though it does not exist in database. In fact if OrderDetailId was supposed to be alternate key, in other words, is unique in the returned set, then you can safely map it as PK
//e.HasNoKey();
e.HasKey(o => o.OrderDetailId);
If it is not unique, then nothing can be done - you cannot map and use navigation property, and will be forced to use manual joins in L2E queries.
Update: EF Core also blocks changing "keyless"-ness once it's been set via fluent API (which has the highest configuration priority). So if you can't remove HasNoKey() fluent call because of it being generated by reverse engineering, you have to resort to metadata API to make it again "normal" entity by setting the IsKeyless property to false before defining the key, e.g.
e.HasNoKey(); // generated by scaffolding
e.Metadata.IsKeyless = false; // <--
e.HasKey(o => o.OrderDetailId); // now this works

EF6: Table Splitting Not Working

I am trying to create an EF6 database where two tables, Addresses and Visits, share the same values as primary keys. Visits, conceptually, is an extension of Addresses. I'm splitting the tables because most of the records in Addresses don't require the fields contained in Visits.
I'm using the code first approach. Here's the relevant code for the Addresses:
public class Address
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[ForeignKey( "ID" )]
public virtual Visit Visit { get; set; }
and for Visits:
public class Visit
{
[Key]
[DatabaseGenerated( DatabaseGeneratedOption.Identity )]
public int ID { get; set; }
[ForeignKey("ID")]
public virtual Address Address { get; set; }
Based on my research, I also needed to include the following in my datacontext's OnModelCreating method:
modelBuilder.Entity<Visit>()
.HasOptional( v => v.Address )
.WithRequired();
Unfortunately, this doesn't work. I can update the database alright, after eliminating scaffolding calls to drop the primary index from Addresses (probably because the add-migration code thinks the primary key is "merely" a foreign key field). But when I run the application I get the following error:
Invalid column name 'Address_ID'.
Invalid column name 'Address_ID'.
From my limited experience with EF6 this looks like someplace deep inside the framework it's expecting there to be fields named 'Address_ID', probably in the Visits table (based on the 'table name'_'field name' naming structure I've seen for other implicitly added fields).
Is what I'm trying to do possible? If so, what am I missing in the configuration?
Additional Info
In trying out bubi's proposed solution, which unfortunately still generates the same error, that I could eliminate the OnModelCreating code and still get functional migration code generated.
Resolution
I finally did what I should've done earlier, which is examine the actual T-SQL code generated by the query which was blowing up. It turns out the problem was not in the Visit/Address linkage, but in a completely separate relationship involving another table. Apparently, somewhere along the way I did something to cause EF to think that other table (Voters) had an Address_ID foreign key field. In reality, the Address/Voter relationship should've been, and originally was, tied to a Voter.AddressID field.
Rather than try to unwind a large number of migrations I opted to blow away the database, blow away the migrations and start from scratch. After recreating the database -- but using bubi's suggestion -- I reloaded the data from backup and, voila, I was back in business.
For the sake of completeness, here's the code I ended up having to put into the OnModelCreating method call to get the Address/Visit relationship to work correctly:
modelBuilder.Entity<Visit>()
.HasRequired( v => v.Address )
.WithRequiredDependent( a => a.Visit );
modelBuilder.Entity<Address>()
.HasRequired( a => a.Visit )
.WithRequiredPrincipal( v => v.Address );
I am a little confused about why I have to use HasRequired in order to be able to use WithRequiredPrincipal/WithRequiredDependent, since not every entry in the Address table has an entry in the Visit table. That would seem to be "optional", not "required". But it appears to work, and maybe the "required" part is just internal to EF's model of the database, not the database itself.
There are 2 problems in the model:
- Only one of the Keys can be autonumbering, the other must get the same Id (this independently by EF).
- A mapping problem.
This model should work.
public class Address
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Description { get; set; }
public virtual Visit Visit { get; set; }
}
public class Visit
{
public Visit()
{
Address = new Address();
}
[Key]
[ForeignKey("Address")]
public int Id { get; set; }
public string Description { get; set; }
public virtual Address Address { get; set; }
}
Example of use
var visit = new Visit
{
Description = "Visit",
Address = {Description = "AddressDescription"}
};
db.Visits.Add(visit);
db.SaveChanges();
In addition to what bubi mentioned, your modelBuilder statement contradicts the model in that it doesn't mention Address.Visit as the inverse property. So it thinks that the property represents a separate relationship and tries to create the Address_ID column for that relationship.
You need to have
modelBuilder.Entity<Visit>()
// from your description sounds like every Visit needs an Address
.HasRequired(v => v.Address )
// need to mention the inverse property here if you have one
.WithOptional(a => a.Visit);
...or just remove the statement completely since you're already using attributes, and EF should be able to figure it out by convention.

Mapping Entity Framework Code First to dynamically named tables

I'm currently using EF5 in a project with a legacy database. The legacy application uses dynamically build tables (xxxx_year, yyyy_year) to store "year based data". I've been trying to find a way to dynamically map the ef entities (xxxx, yyyy, etc) to the tables, based on the year property value, but I always end up getting the "The model backing the context has changed since the database was created." error. Can anyone give me some ideas on how to accomplish this ?
I found some old blog posts talking about edm mapping, where we can separate mapping tables based on some property value (kind of horizontal partitioning), but I can't find any pointers on how to accomplish the same using code first.
Thanks, P
In your mapping configuration for each domain object, you can tell EF that the corresponding table name for an entity is different from the entity name itself.
If your class is called YyyyYear, it can point to a table called "2012_year" by specifying the name in its mapping file.
e.g.
// 1 entity class per db table
public class YyyyYear
{
public int Id { get; set; }
}
// 1 mapping file for entity
using System.Data.Entity.ModelConfiguration;
public class YyyyYearMap: EntityTypeConfiguration
{
public YyyyYearMap()
{
this.HasKey(t => t.Id);
this.ToTable("2012_year");
}
}
// your db context class (derives from DbContext)
using System.Data.Entity;
public class MyDbContext: DbContext
{
// 1 db set for every entity/table
public DbSet YyyyYears { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// 1 mapping file for every entity/table
modelBuilder.Configurations.Add(new YyyyYearMap());
}
}
I'm not sure if that's what you're looking for, but I have a blog post with step-by-step instructions, a working sample, and how to resolve common issues.
http://wakeupandcode.com/entity-framework-code-first-migrations/
Hope this helps!

Create an Updatable Model - Entity Framework

I just want to know if there's a way on how to create an Updatable model. Right now, I have to create procedures for insert, update, and delete for all of the tables in my model. This is very tedious so I was wondering if there is one way which I could do to resolve this?
I remember before in my previous work that we used to make models and access them (CRUD) without creating procedures. But i'm not really certain now on how it was made.
Thank you!
There are various ways in which you can automate the generation (on the fly or already generated at compile time) of the actual SQL calls to the database to insert, select, update and delete within the Entity Framework.
You can use the ORM tools (e.g. Linq to Entities) to minimise or eliminate the writing of raw SQL. This means you still have to use the correct attributes on your entities and the properties/methods therein and that's a manual process. (Some backgrounding on this MSDN page)
You can allow the framework to automatically generate your entities based on some existing database schema (only possible with SqlServer-type databases) which basically does 90% of the work for you. There may be some cases where you need to override, for example, the default insert SQL with something custom. This is achieved via the Generate Database Wizard (which I think is a part of Visual Studio 2008+).
You can use POCO classes with EF. If you're using 4.1 and above, you can use the DbContext class. To map your model to the table / columns, simply override OnModelCreating in your context class (which inherits from DbContext). Say you have a model called User, a table called Users, and the context class MyContext, the code could be smth like this:
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
}
public class MyContext : DbContext
{
public MyContext() :
base("MyContext")
{
}
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().
.ToTable("Users");
modelBuilder.Entity<User>().
.Property(d => d.UserId)
.HasColumnName("UserId")
modelBuilder.Entity<User>().
.Property(d => d.UserName)
.HasColumnName("UserName");
}
}
To use it, simply add the User instance to your DbSet, then call SaveChanges:
using(MyContext ctx = new MyContext())
{
var u = new User() { UserId = 1, UserName = "A" };
ctx.Users.Add(u);
ctx.SaveChanges();
}

Entity Framework reports circular reference, but it doesn't make any sense

Here is the approx. code I am working with.
public class Note {
public virtual Customer Customer { get; set; }
public virtual User User { get; set; }
public ICollection<NoteComment> Comments { get; set; }
}
public class NoteComment {
public virtual User User { get; set; }
}
public class User {
public ICollection<Note> Notes { get; set; }
}
public class Customer {}
// --------------------------------------
public class OurDataContext {
private void ConfigureNotes(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Note>()
.HasRequired<User>(n => n.User)
.WithMany(u => u.Notes)
.Map(a => a.MapKey("UserId"));
modelBuilder.Entity<Note>()
.HasRequired(n => n.Customer)
.WithMany(c => c.Notes)
.Map(a => a.MapKey("idCustomer"));
modelBuilder.Entity<Note>()
.HasMany(n => n.Comments)
.WithRequired()
.HasForeignKey(c => c.NoteID);
/*
modelBuilder.Entity<NoteComment>()
.HasRequired<User>(c => c.User)
.WithMany()
.Map(a => a.MapKey("UserId"));
*/
}
}
}
Note that in the ConfigureNotes() method, the last configuration is commented out. If I leave this commented out, EF will create my tables just fine, but if I uncomment this block, I get the following error:
Unhandled Exception: System.InvalidOperationException: The database creation succeeded, but the creation of the database objects did not. See inner exception for more details. ---> System.Data.SqlServerCe.SqlCeException: The referential relationship will result in a cyclical reference that is not allowed. [ Constraint name = Note_Comments ]
at System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32 hr)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommandText(IntPtr& pCursor, Boolean& isBaseTableCursor)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior, String method, ResultSetOptions options)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteNonQuery()
at System.Data.SqlServerCe.SqlCeProviderServices.DbCreateDatabase(DbConnection connection, Nullable`1 timeOut, StoreItemCollection storeItemCollection)
--- End of inner exception stack trace ---
...
What I don't understand is why the navigation property from NoteComment => User is generating a circular reference between Note => NoteComment.
EDIT
For some reason, specifying the FK in the NoteComment class as a nullable property fixed the problem.
public class NoteComment {
public Guid? UserId { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
}
Then I removed the commented out mapping code in the data context class.
This is not ideal, but I can manage this constraint manually.
SQL Server is very conservative about possible circular references or multiple delete paths compared to other databases.
Yours is originating from multiple delete paths to NoteComment:
Delete User -> Note -> NoteComment
Delete User -> NoteComment
One solution is to remove the Cascade On Delete for User -> NoteComment and do the cleanup manually.
You can also write a database trigger to do the cleanup. Here's an example trigger:
CREATE TRIGGER [dbo].[Users_Delete_Cleanup]
ON [dbo].[Users]
INSTEAD OF DELETE
AS
BEGIN
IF ##ROWCOUNT = 0
RETURN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Delete NoteComment <--> User associations
DELETE nc FROM [dbo].[NoteComment] nc
JOIN DELETED dUser ON dUser.[Id] = nc.[User_Id]
-- Finally, delete user
DELETE u
FROM DELETED dUser
JOIN [dbo].[Users] u ON u.[Id] = dUser.[Id]
END
Edits - additional information:
If you don't have it already, I highly suggest the EF Power Tools extension. This gives you the ability to right click on any class implementing DbContext and get the Entity Framework context menu -
Right click your DbContext class in Solution Explorer -> Entity Framework -> View DDL SQL
That will give you the Sql statement used to generate your entire data model - very useful indeed to see what exactly EF thinks it's building. You can try and run it manually in SqlServer and get a bit closer to the errors that it's running into. When EF is building up the DDL Sql, short of compile errors it will usually get you something (or an entirely cryptic null reference error - then check your Output window), but that something might not run in SqlServer.
Also, you can manually remove the cascade on delete for one to many relationships with the fluent configuration, no need to specify keys unless you want that property:
modelBuilder.Entity<NoteComment>()
.HasRequired<User>(c => c.User)
.WithMany()
.WillCascadeOnDelete(false);
You have a circular reference between User->Note Comments->Note->User->etc..
The you have the Keys setup you you will never stop referencing in the above manner.
There are many different methods to end a circular reference. Such as, [ScriptIgnore] attribute above the properties that are causing a circular reference, or you can do a tree search method where each branch checks to make sure that the object being added is not a parent node (recursively) all the way to the root node of the tree.