SqlException: On Delete Cascade not working ASP.NET - asp.net

When I try to delete a user from the ASP.NETUsers table I get SqlException:
SqlException: The DELETE statement conflicted with the REFERENCE
constraint "FK_Applications_AspNetUsers_UserID". The conflict occurred
in database "JobGuide", table "dbo.Applications", column 'UserID'.
This problem is occurring because the User's Id is the Foreign key in another table, but "On delete cascade" is not working. For more details this is my model:
My extended Identity User:
public class AppUser : IdentityUser
{
public string Name { get; set; }
public string City { get; set; }
public string RoleName { get; set; }
public virtual ICollection<Application> Applications { get; set; }
}
Application model (i.e. when a user applies for a job):
public class Application
{
public int ApplicationID { get; set; }
public int JobID { get; set; }
public virtual Job Job { get; set; }
public string UserID { get; set; }
public virtual AppUser User { get; set; }
}
Job model:
public class Job
{
public int JobID { get; set; }
public string Title { get; set; }
public virtual ICollection<Application> Applications { get; set; }
}
So up to here I created two One to Many relationships, AspNetUser one to many with Application and Job one to many with Application.
And this is my Fluent API mapping configuration:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Application>()
.HasKey(i => i.ApplicationID);
builder.Entity<Application>()
.HasOne<AppUser>(sc => sc.User)
.WithMany(s => s.Applications)
.HasForeignKey(sc => sc.UserID)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<Application>()
.HasOne<Job>(sc => sc.Job)
.WithMany(s => s.Applications)
.HasForeignKey(sc => sc.JobID)
.OnDelete(DeleteBehavior.Cascade);
}
Delete method from controller:
var userInfo = await userManager.FindByIdAsync(user.Id);
if (userInfo == null)
{
return NotFound();
}
_ = await userManager.RemoveFromRoleAsync(userInfo, userInfo.RoleName);
_ = await userManager.DeleteAsync(userInfo);
int rowsAffected = await db.SaveChangesAsync();
Any idea why this error is not disappearing, is Fluent API good? or i need to type raw Sql to delete the Application with that User once and then the User? I have looked at almost all similar questions but none of them are working for me.

It seems that the cascade delete is not configured in the application table, try to use SSMS to check it:
Open the SQL Server Object Explorer (or using Server Explorer), find the SQL Server Database, then right click the Applications table -> Script As -> CREATE To -> New Query Window, then check whether the table is configured Cascade delete, check this screenshot:
To solve this issue, after configuration Cascade Delete using Fluent API mapping, please remember to enable migration and update the database:
Add-Migration AddCascadeDelete
Update-Database
Besides, you could also configure the Cascade Delete by executing the following SQL command (via SSMS):
ALTER TABLE [dbo].[Applications]
ADD CONSTRAINT [FK_Applications_AspNetUsers_UserID] FOREIGN KEY ([UserID]) REFERENCES [dbo].[AspNetUsers] ([Id]) ON DELETE CASCADE;

Can you try setting it the other way around;
builder.Entity<User>()
.HasMany<Application>(u => u.Applications)
.WillCascadeOnDelete();
or use .WillCascadeOnDelete() on your code.

Related

ApplicationUser has a list of ApplicationUser

I have built a new Web Application that uses the template Visual Studio provides and included MVC and Web API. The default authorization mechanism is Identity and the database interaction is done using Entity Framework with Code-first method of creating the database.
I have three requirements:
A user can have a list of Children objects
I do not want to use a "relationship" object
All users already exist on the AspNetUsers table, because they all need to be able to login, so I do not want another table to maintain user data
In theory, multiple parents could have reference to multiple children, but for this example, we will just consider it a one-to-many relationship.
In my application, I need to have an ApplicationUser have a list of ChildUsers as a collection of ApplicationUser such as shown below.
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public string ShirtSize { get; set; }
public ICollection<ApplicationUser> Children { get; set; }
}
I want these users to be accessible as shown above (a collection of ApplicationUser), not a collection of Relationship object that ties them together such as:
public class Relationship
{
public String ParentId { get;set; }
public String ChildId { get;set; }
}
Can a new table be created and exist on the database without having a code-first model for it to know how to create a relationship table?
What are available solutions to this problem?
After some research, and experimentation, I have found bits and pieces of guidance to arrive at a solution that works.
In order for an intermediate table to be created to maintain the relationship, the ApplicationDbContext OnModelCreating function needs to know what it should look like. I have told it to create a new table that is not bound to an object by using the modelBuilder shown in the code below. Unfortunately, I do not have the links to the articles that guided me to this.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base( "DefaultConnection", throwIfV1Schema: false )
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
base.OnModelCreating( modelBuilder );
modelBuilder.Entity<ApplicationUser>()
.HasMany( p => p.ChildUsers )
.WithMany()
.Map( m =>
{
m.MapLeftKey( "Father_Id" );
m.MapRightKey( "Son_Id" );
m.ToTable( "father_son_relation" );
} );
}
}
Additionally, when you need to add Children to the parent ApplicationUser, you will need to do some tweaking as you are about to insert so that it updates the database correctly. I definitely want the UserManager to do the creation of the user for me, but that means that when I go to add the user to my list of Children with the code below, it tries to add it again and throws an exception because it already exists.
var result = await UserManager.CreateAsync( user, model.Password );
var myUserId = User.Identity.GetUserId();
var users = AppDbContext.Users.Where( u => u.Id == myUserId ).Include( u => u.ChildUsers );
var u2 = users.First();
u2.ChildUsers.Add( user );
await AppDbContext.SaveChangesAsync();
After finding this question, I researched the EntityStates and found that adding the following line before calling SaveChanges resolved the exception and it no longer attempts to add it again.
AppDbContext.Entry( user ).State = EntityState.Unchanged;
TADA!!! Now to select them from the database using EF, you can then use the following code:
AppDbContext.Users.Where( u => u.Id == myUserId ).Include( u => u.Children ).First();
Since I am only getting one level of Children this will work ok, after that you risk circular references.
Comments and ideas to improve the code are welcome.

ef core migration insert data

I'd like to insert data into table in migration. Is it possible? Migration needs parameterless constructor available and I'd like to use db context defined in Startup.cs file (best I'd like to get it throught dependency injection). How do that?
In the EF Core 2.1, migrations can automatically compute what insert, update or delete
operations must be applied when upgrading the database to a new version of the model.
As an example, we have a User and UserComment entities like these:
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public List<UserComment> UserComments { get; set; }
}
public class UserComment
{
[Key]
public int CommentId { get; set; }
public string CommentTitle { get; set; }
[ForeignKey("User")]
public int FKUserId { get; set; }
public User User { get; set; }
}
In the DBContext, override the OnModelCreating function and seed data to each entity:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasData(new User[] {
new User{UserId=1,Name="iman"},
new User{UserId=2,Name="Alex"},
});
}
To seed datas that have a relationship, the foreign key value must be specified:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserComment>().HasData(new UserComment[] {
new UserComment{FKUserId=1,CommentId=1,CommentTitle="Comment1"},
});
}
Be careful: you must use migrations to apply changes
Migration is a process of "upgrading" your DB to a new "version". During this, your existing DB tables ("old version") does not required to match your classes (entities) ("new version"), so you can't safely use them.
During migration you should operate only with tables and records using raw SQL commands. You may use migrationBuilder.Sql("UPDATE ..."); for such updates, put them manually into migration Up() code.
If you need perform data modifications using entity classes - you should use "Seed Data" solution (from #itikhomi comment), but remember that it will be run every time your app starts, so you should do some version-check inside it.

Extending Identity3 in MVC6

using the latest (current) RC1 of asp.net5 I'm looking at creating a simple relationship between a User entity and a WorkLog entity.
Is it possible to use the ApplicationUser Class from Identity as a starting point and use the ApplicationUser key which is defined as the linking key? I have had problems extending the ApplicationUser in the past and therefore generated a seperate dbcontext (pointing to the same database) and created my own plumbing in order to pass the IdentityUsers Id into my seperate dbcontext. Does anyone have any examples of extending the IdentityDbContext adding foreign key tables mapping to the IdentityUser Class?
Example below
//DBContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<WorkLogItem> WorkLogItems { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
builder.Entity<WorkLogItem>(
e =>
{
e.Property(p => p.id).IsRequired().UseSqlServerIdentityColumn();
});
}
}
//WorkLogItem
public class WorkLogItem
{
public int id { get; set;}
public String UserId { get; set; }
public int Hours { get; set; }
public String Description { get; set; }
}
//ApplicationUser
public class ApplicationUser : IdentityUser
{
public ICollection<WorkLogItem> WorkLogItems { get; set; }
}
Doing what you've asked is expected to work out of the box. You can look at this commit to see the difference between a newly created MVC 6 project with Identity and your schema above.
Registering a user, and refreshing /Home/Index causes WorkLogItems to be added as expected. Note you don't need a separate DB context for this.
public IActionResult Index()
{
var user = _db.Users.Include(p => p.WorkLogItems).FirstOrDefault();
if (user != null)
{
user.WorkLogItems.Add(new WorkLogItem { Description = "New item added" });
_db.SaveChanges();
ViewBag.WorkItems = user.WorkLogItems.ToList();
}
else ViewBag.WorkItems = new WorkLogItem[] { };
return View();
}
The key items to be aware of when you add any collection to an existing entity are;
Make sure you add the migration and update the databse
Make sure you use Include on the query because EF7 does not support Lazy Loading.

Add a missing table

I miss a table in my data model
I have an existing DB, and tried:
EF Power Tools like THIS description
tables:
User (Id, ...)
Project (ID, Name,..)
Timestamp (ID,Start, End, UserID, ProjectID..)
But I have no Table "Poject_Favorit" (UserId, ProjectID)
So I write the Table myself:
public class Project_Favorite
{
[Key]public Guid GuidUser { get; set; }
public Guid GuidProject { get; set; }
}
and I added:
public DbSet<Project_Favorite> Project_Favorite { get; set; }
to my DbContext
EF searched for "dbo.Project_Favorite" but it should search for "Timeworx.Project_Favorite"
so I added:
modelBuilder.Configurations.Add(new Project_FavoriteMap());
to my DbContext
and created the file "Project_FavoriteMap.cs:
class Project_FavoriteMap : EntityTypeConfiguration<Project_Favorite>
{
public Project_FavoriteMap() {
//Primary Key
this.HasKey(t => t.GuidProject);
this.HasKey(t => t.GuidUser);
this.ToTable("Project_Favorite", "Timeworx");
}
}
Now there is an error. It says "...Project_Favorite already defined..."
It looks like the Poject_Favorit table is a many-to-many join between User and Project. In that case you don't need a corresponding entity class. Your User class will probably have an ICollection<Project> and your Project class will probably have an ICollection<User>. That represents a many-to-many hierarchy. A project can favorited by many users and a user can favorite many projects. Hence the two collections.

EF 4.1 messing things up. Has FK naming strategy changed?

I've just installed the new Entity Framework 4.1 NuGet package, thus replacing the EFCodeFirst package as per NuGet intructions and this article of Scott Hanselman.
Now, imagine the following model:
public class User
{
[Key]
public string UserName { get; set; }
// whatever
}
public class UserThing
{
public int ID { get; set; }
public virtual User User { get; set; }
// whatever
}
The last EFCodeFirst release generated a foreign key in the UserThing table called UserUserName.
After installing the new release and running I get the following error:
Invalid column name 'User_UserName'
Which of course means that the new release has a different FK naming strategy. This is consistent among all other tables and columns: whatever FK EFCodeFirst named AnyOldForeignKeyID EF 4.1 wants to call AnyOldForeignKey_ID (note the underscore).
I don't mind naming the FK's with an underscore, but in this case it means having to either unnecessarily throw away the database and recreate it or unnecessarily renaming al FK's.
Does any one know why the FK naming convention has changed and whether it can be configured without using the Fluent API?
Unfortunately, one of the things that didn't make it to this release is the ability to add custom conventions in Code First:
http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-release-candidate-available.aspx
If you don't want to use the fluent API to configure the column name (which I don't blame you), then most straight forward way to do it is probably using sp_rename.
Why don't you do the following?
public class User
{
[Key]
public string UserName { get; set; }
// whatever
}
public class UserThing
{
public int ID { get; set; }
public string UserUserName { get; set; }
[ForeignKey("UserUserName")]
public virtual User User { get; set; }
// whatever
}
Or, if you don't want to add the UserUserName property to UserThing, then use the fluent API, like so:
// class User same as in question
// class UserThing same as in question
public class MyContext : DbContext
{
public MyContext()
: base("MyCeDb") { }
public DbSet<User> Users { get; set; }
public DbSet<UserThing> UserThings { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<UserThing>()
.HasOptional(ut => ut.User) // See if HasRequired fits your model better
.WithMany().Map(u => u.MapKey("UserUserName"));
}
}

Resources