The DELETE statement conflicted with a REFERENCE constraint - asp.net

I have the following entities.
public class GroupUser
{
public long Id { get; set; }
public Group Group { get; set; }
public long GroupId { get; set; }
public string UserId { get; set; }
public User User { get; set; }
}
public class User : IdentityUser
{
public List<GroupUser> GroupUsers { get; set; }
}
public class Group
{
public long Id { get; set; }
public List<GroupUser> GroupUsers { get; set; }
}
I get the delete The DELETE statement conflicted with the REFERENCE constraint "FK_GroupUsers_AspNetUsers_UserId". The conflict occurred in database "db", table "dbo.GroupUsers", column 'UserId' when I try to delete a user even though I make sure i delete existing entities referencing the id of the user before actually deleting the user like below;
var user = await _userManager.FindByEmailAsync(email);
var groupUsers = _context.GroupUsers.Where(c => c.UserId == user.Id);
_context.RemoveRange(groupUsers);
await _context.SaveChangesAsync();
_context.Remove(user);
await _context.SaveChangesAsync();

Related

EF6 Many-to-many can not delete

I have created table using EF6 core using below code.
The problem is, UserA , UserB created and assigned the different claims. No issue at all. When assigning existing claims (for example UserB is assigned UserA's claims --> [tying to do] delete UserB's claims & Changing UserB's subject to UserA's subject) then I am deleting UserB's claim by subjectId this create the primary key duplicate issue in UserUserClaims tables bcos SubjectId and ClaimId already existing
public class User
{
[Key]
public Guid UserId { get; set; }
[MaxLength(200)]
public string Username { get; set; }
[Required]
public Guid Subject { get; set; }
public ICollection<UserClaim> Claims { get; set; } = new List<UserClaim>();
public List<UserClaimRelation> UserClaimRelations { get; set; }
}
public class UserClaim
{
[Key]
public Guid UserClaimId { get; set; }
[MaxLength(250)]
[Required]
public string Type{ get; set; }
[Required]
public Guid Subject { get; set; }
[MaxLength(250)]
[Required]
public string Value { get; set; }
public ICollection<User> Users { get; set; }
public List<UserClaimRelation> UserClaimRelations { get; set; }
}
public class UserClaimRelation
{
public Guid UserClaimId{ get; set; }
public UserClaim Claim { get; set; }
public Guid UserSubject{ get; set; }
public User User { get; set; }
}
public class MyDbContext: DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<UserClaim> UserClaims { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasMany(p => p.Claims)
.WithMany(p => p.Users)
.UsingEntity<UserClaimRelation>(
j => j
.HasOne(pt => pt.Claim)
.WithMany(t => t.UserClaimRelations)
.HasPrincipalKey(f => f.UserClaimId),
j => j.HasOne(p => p.User)
.WithMany(t => t.UserClaimRelations)
.HasPrincipalKey( t => t.Subject),
j => j.HasKey( t => new {t.UserClaimId, t.UserSubject})
);
}
I am trying to delete the Claims by SubjectId
public async Task<bool> DeleteUserClaimsBySubjectAsync(Guid subject)
{
if (subject == Guid.Empty)
{
throw new ArgumentNullException(nameof(subject));
}
var claims = await _context.UserClaims
.Where(x => x.Subject == subject).ToListAsync();
foreach (var claim in claims)
{
_context.UserClaims.Remove(claim);
}
//_context.UserClaims.RemoveRange(claims);
return (await _context.SaveChangesAsync() > 0);
}
While deleting claims in DeleteUserClaimsBySubjectAsync I am getting the below error
SqlException: Violation of PRIMARY KEY constraint 'PK_UserClaimRelation'. Cannot insert duplicate key in object 'dbo.UserClaimRelation'. The duplicate key value is (13229d33-99e0-41b3-b18d-4f72127e3971, 8acbbd40-1608-41f2-de59-08d9b58b83d3).
SQL Profiler Query
exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [UserClaimRelation] ([UserClaimId], [UserSubject])
VALUES (#p0, #p1),
(#p2, #p3),
(#p4, #p5),
(#p6, #p7),
(#p8, #p9);
DELETE FROM [UserClaims]
WHERE [UserClaimId] = #p10 AND [ConcurrencyStamp] = #p11;
SELECT ##ROWCOUNT;
DELETE FROM [UserClaims]
WHERE [UserClaimId] = #p12 AND [ConcurrencyStamp] = #p13;
SELECT ##ROWCOUNT;
DELETE FROM [UserClaims]
WHERE [UserClaimId] = #p14 AND [ConcurrencyStamp] = #p15;
SELECT ##ROWCOUNT;
DELETE FROM [UserClaims]
WHERE [UserClaimId] = #p16 AND [ConcurrencyStamp] = #p17;
SELECT ##ROWCOUNT;
DELETE FROM [UserClaims]
WHERE [UserClaimId] = #p18 AND [ConcurrencyStamp] = #p19;
SELECT ##ROWCOUNT;
',N'#p0 uniqueidentifier,#p1 uniqueidentifier,#p2 uniqueidentifier,#p3 uniqueidentifier,#p4 uniqueidentifier,#p5 uniqueidentifier,#p6 uniqueidentifier,#p7 uniqueidentifier,#p8 uniqueidentifier,#p9 uniqueidentifier,#p10 uniqueidentifier,#p11 nvarchar(4000),#p12 uniqueidentifier,#p13 nvarchar(4000),#p14 uniqueidentifier,#p15 nvarchar(4000),#p16 uniqueidentifier,#p17 nvarchar(4000),#p18 uniqueidentifier,#p19 nvarchar(4000)',
#p0='6203BBC3-BD52-4E32-3DDD-08D9B86AB745',#p1='D860EFCA-22D9-47FD-8249-791BA61B07C7',
#p2='80B958C5-FF44-400F-3DDE-08D9B86AB745',#p3='D860EFCA-22D9-47FD-8249-791BA61B07C7',
#p4='A844CB59-5A00-488A-3DDF-08D9B86AB745',#p5='D860EFCA-22D9-47FD-8249-791BA61B07C7',#p6='794AB806-3215-45AF-3DE0-08D9B86AB745',#p7='D860EFCA-22D9-47FD-8249-791BA61B07C7',#p8='80EFF0DF-AE56-419A-3DE1-08D9B86AB745',#p9='D860EFCA-22D9-47FD-8249-791BA61B07C7',#p10='1A48CF6E-E1CD-4042-3DE4-08D9B86AB745',#p11=N'742674d8-3c59-4be4-ac09-38c6804acb66',#p12='258ABD95-6B2A-45CC-3DE6-08D9B86AB745',#p13=N'd206316a-71af-46b7-92e4-bddca669ad87',#p14='4FB1D2E3-18BE-4AFC-3DE3-08D9B86AB745',#p15=N'3eb17fb7-998b-47e1-a97f-3640cbd82b7a',#p16='6C2B9A18-5C8F-4068-3DE5-08D9B86AB745',#p17=N'415b2cc1-b50a-4ec8-872d-13b6342dcd33',#p18='951CEEA6-D057-4588-3DE2-08D9B86AB745',#p19=N'3dfd76dd-d515-4daa-893c-f1ae97aa063a'
Ok, so you want many-to-many relationship here - multiple user can have multiple different claims and multiple claims can be assigned to multiple users.
So first - Users:
public class User
{
[Key]
public Guid UserId { get; set; }
[MaxLength(200)]
public string Username { get; set; }
[Required]
public Guid Subject { get; set; }
public ICollection<UserClaim> Claims { get; set; } = new List<UserClaim>();
}
Then, UserClaims
public class UserClaim
{
[Key]
public Guid UserClaimId { get; set; }
[MaxLength(250)]
[Required]
public string Type{ get; set; }
[Required]
public Guid Subject { get; set; }
[MaxLength(250)]
[Required]
public string Value { get; set; }
public ICollection<User> Users { get; set; }
}
Notice that there is no navigation property leading to UserClaimRelation in any of the above. In the matter of fact, we remove this UserClaimRelation as an separate entity model entirely - it is not needed for this example. You can map it if you really want, but lets focus on making this work.
Now, the relationship setup:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany(x => x.Claims)
.WithMany(x => x.Users)
.UsingEntity<Dictionary<string, object>>(
"UserClaimRelation",
x => x.HasOne<User>().WithMany(),
x => x.HasOne<UserClaim>().WithMany(),
x => x.ToTable("UserClaimRelation"));
}
Now, after updating / recreating / reapplying migrations to the database (depends on your way of doing things) you should be able to delete the claims by subject without modifying your DeleteUserClaimsBySubjectAsync method.
This should set it all up and create intermediary table.
Validate this code, I was typing from memory
I applied the code. Tables created like below image. But when I try to update an userA's subject with userB's Subject (same claims of userB) I got the below error
Updating
SqlException: Violation of PRIMARY KEY constraint 'PK_UserClaimRelation'. Cannot insert duplicate key in object 'dbo.UserClaimRelation'. The duplicate key value is (bd7eb87f-2a5f-4921-758e-08d9b97d3121, 13229d33-99e0-41b3-b18d-4f72127e3971). bcos its trying to insert in UserClaimRelation, same as the profiler query I given initially.

EF Core 3.1.4 Navigation property returning null in One-To-One relationship

I am having a problem where a navigation property is always returning null in a One-to-One relationship in EF Core 3.1.4.
My models are structured like so:
public class UserCredential
{
public Guid Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public byte[] Salt { get; set; }
public bool IsLocked { get; set; }
public bool IsDeleted { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? ModifiedDate { get; set; }
public DateTime? DeletedDate { get; set; }
public UserProfile UserProfile { get; set; }
}
public class UserProfile
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string Suffix { get; set; }
public bool IsDeleted { get; set; }
public List<Address> Addresses {get;set;}
public DateTime DOB { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? ModifiedDate { get; set; }
public DateTime? DeletedDate { get; set; }
public Guid UserCredentialId { get; set; }
public UserCredential UserCredential { get; set; }
}
Based off what I understand, that should have been enough for EF Core to infer the One-To-One relationship. But it didnt work so I defined the relationshup in my dbContext like so:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<UserProfile>(entity =>
{
entity.HasOne(up => up.UserCredential)
.WithOne(uc => uc.UserProfile)
.HasForeignKey<UserProfile>( up => up.UserCredentialId);
});
}
I checked the db and there is a foreign key from UserProfile -> UserCredentials defined in the table. Likewise both tables have Id as a primary key.
If I post data to a "addUser" endpoint it will be added correctly in the db.
{
"username": "test3",
"password": "password123",
"UserProfile":{
"FirstName": "John",
"LastName": "Doe"
}
}
Db Screenshot
However "UserProfile" in my model is always null.
System.NullReferenceException: 'Object reference not set to an
instance of an object.'
IronCarp.Identity.Models.UserCredential.UserProfile.get returned null.
I'm using a repository to interact with the db and the method that is returning my data/model seems simple enough.
private async Task<UserCredential> GetUserCredentials(string username)
{
return await context.UserCredentials.Where(u => u.Username == username).FirstOrDefaultAsync();
}
Any help is appreciated, I am not sure what I am missing, thanks!
try to include navigation property in linkq, try something like this:
private async Task<UserCredential> GetUserCredentials(string username)
{
return await context.UserCredentials.Include(x => x.UserProfile).Where(u => u.Username == username).FirstOrDefaultAsync();
}
For me this issue was happening intermittently in my integration tests.
It turned out because I was mistakenly registering the EF Core DbContext as Transient and not Scoped:
Services.AddDbContext<SubscriptionsContext>(
options => options.UseNpgsql(appConfig.DatabaseConnectionString,
optionsBuilder => optionsBuilder.EnableRetryOnFailure(3)), ServiceLifetime.Transient);
Should be:
Services.AddDbContext<SubscriptionsContext>(
options => options.UseNpgsql(appConfig.DatabaseConnectionString,
optionsBuilder => optionsBuilder.EnableRetryOnFailure(3)), ServiceLifetime.Scoped);
If you look at the default parameter on the AddDbContext() extension method, it's always Scoped:

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.ConsoleUserInfoes_dbo.ConsolesCheckBoxes_consoleId"

I'm getting this error:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.ConsoleUserInfoes_dbo.ConsolesCheckBoxes_consoleId". The conflict occurred in database "aspnet-ForePlay-20180525122039", table "dbo.ConsolesCheckBoxes", column 'ConsoleId'.
I'm using Entity Framework and ASP.NET MVC 5 and IdentityUser and try to insert data form checkListBox to table into my database.
This is happening on the register view, when user need to register and fill the form.
public class ConsoleUserInfo
{
[Key]
public int identity { get; set; }
[Required]
[StringLength(255)]
[ForeignKey("User")]
public string userid { get; set; }
[Required]
[ForeignKey("consolesCheckBox")]
public int consoleId { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual ConsolesCheckBox consolesCheckBox { get; set; }
}
This is the table that need to get a user id (form applictionUser) and consoleId
(form ConsolesCheckBox )
This is the ApplicationUserUser model class:
public class ApplicationUser : IdentityUser
{
[Required]
[StringLength(255)]
override
public string UserName { get; set; }
[Required]
[StringLength(50)]
public string Phone { get; set; }
public byte[] UserPhoto { get; set; }
public virtual UserAddress Address { get; set; }
public virtual ICollection<ConsolesCheckBox> consoleCheckBox { get; set; }
}
and this is the checkBoxList table:
public class ConsolesCheckBox
{
[Key]
public int ConsoleId { get; set; }
public string ConsoleName { get; set; }
public bool IsChecked { get; set; }
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
}
This is my account controller, all in the register get and post
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
//using database
using (ApplicationDbContext dbo = new ApplicationDbContext())
{
//data will save list of the consoleCheckBoxItem
var data = dbo.consolesCheckBox.ToList();
// because the view is request a common model, we will create new one
CommenModel a = new CommenModel();
a.ConsolesCheckBoxList = data;
// we will need to return common model, that way we will return a
return View(a);
}
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register([Bind(Exclude = "UserPhoto")]CommenModel model)
{
if (ModelState.IsValid)
{
// To convert the user uploaded Photo as Byte Array before save to DB
byte[] imageData = null;
if (Request.Files.Count > 0)
{
HttpPostedFileBase poImgFile = Request.Files["UserPhoto"];
using (var binary = new BinaryReader(poImgFile.InputStream))
{
imageData = binary.ReadBytes(poImgFile.ContentLength);
}
}
var user = new ApplicationUser
{
UserName = model.registerViewModel.Email,
Email = model.registerViewModel.Email,
Phone = model.registerViewModel.Phone
};
user.UserPhoto = imageData;
var result = await UserManager.CreateAsync(user, model.registerViewModel.Password);
//after the user create, we will use the id and add the id to the userAddress table include
// Address, longitude and latitude.
using (ApplicationDbContext dbo = new ApplicationDbContext())
{
var currentUserId = user.Id;
var pasinfo = dbo.userAddress.FirstOrDefault(d => d.Userid == currentUserId);
if (pasinfo == null)
{
pasinfo = dbo.userAddress.Create();
pasinfo.Userid = currentUserId;
dbo.userAddress.Add(pasinfo);
}
pasinfo.Address = model.useraddress.Address;
pasinfo.latitude = model.useraddress.latitude;
pasinfo.longitude = model.useraddress.longitude;
dbo.SaveChanges();
foreach (var item in model.ConsolesCheckBoxList.Where(x => x.IsChecked).Select(x => x.ConsoleId))
{
var consoleUserInfo = new ConsoleUserInfo
{
userid = currentUserId,
consoleId = item
};
dbo.consoleUserInfo.Add(consoleUserInfo);
}
dbo.SaveChanges();
}
}
}
In the register GET I have a common model, because I used 3 models in the view
this is the common model:
public class CommonModel
{
public UserAddress useraddress { get; set; }
public RegisterViewModel registerViewModel { get; set; }
public List<ConsolesCheckBox> ConsolesCheckBoxList { get; set; }
}
I need your help here, I've been trying to fix this all day.

Many to many with extra foreign key?

I want to generate a junction table between user and post and I want to have the userId in the post table.
I have this code
public class Post
{
public Post()
{
this.ApplicationUser = new HashSet<ApplicationUser>();
}
public int PostId { get; set; }
public string Message { get; set; }
public DateTime MessageDate { get; set; }
public virtual ApplicationUser User { get; set; } //this is the problem
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
}
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public ApplicationUser()
{
this.Posts = new HashSet<Post>();
}
public virtual ICollection<Post> Posts { get; set; }
}
I get the extra junction table and many-to-many relation between user and post. But this is wrong.
public virtual ApplicationUser User { get; set; }
This generates two UserId in the post table (applicationUser_id and User_id) and Post_PostId in the User table. I just want one extra field in the Post table, FK UserId.
I want three tables like this
Post
PostId
Message
Date
UserId FK
User
UserId
And the rest of the fields in asp.net identity user
UserPosts
UserId
PostId
User table
public partial class User
{
public User()
{
this.Posts = new HashSet<Post>();
this.UserPosts = new HashSet<UserPost>();
}
public int UserId { get; set; }
public string Username { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual ICollection<UserPost> UserPosts { get; set; }
}
Post table
public partial class Post
{
public Post()
{
this.UserPosts = new HashSet<UserPost>();
}
public int PostId { get; set; }
public string Message { get; set; }
public Nullable<System.DateTime> Date { get; set; }
public Nullable<int> UserId { get; set; }
public virtual User User { get; set; }
public virtual ICollection<UserPost> UserPosts { get; set; }
}
and your mapping table, like this
your column 1) Id (pk), 2) UserId (fk) 3) PostId (fk)
using entity framework table have one primary key necessary.
UserPost table
public partial class UserPost
{
public int Id { get; set; }
public Nullable<int> UserId { get; set; }
public Nullable<int> PostId { get; set; }
public virtual Post Post { get; set; }
public virtual User User { get; set; }
}
Updated Code
modelBuilder.Entity<Post>().ToTable("userid table name");
this line add in below method of this class ApplicationDbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}

How to join my tables with identity tables?

I started a default MVC project with Identity and EF.
In my app users will be able to create and edit some records.
In the table for these records, I want to have the ids of users who created the record and who updated lastly.
My model class is like:
public class Record
{
public int ID { get; set; }
public DateTime CreateTime { get; set; }
public string CreatingUserID { get; set; }
public string UpdatingUserID { get; set; }
public DateTime UpdateTime { get; set; }
public Enums.RecordStatus Status { get; set; }
}
And in RecordsController, I save new records to db like this:
[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(FormCollection form, RecordCreateVM vm)
{
string userId = User.Identity.GetUserId();
DateTime now = DateTime.Now;
Record rec = new Record ();
if (ModelState.IsValid)
{
int newRecordId;
using (RecordRepository wr = new RecordRepository())
{
UpdateModel(rec);
rec.CreateTime = now;
rec.UpdateTime = now;
rec.CreatingUserID = userId;
rec.UpdatingUserID = userId;
rec.Status = Enums.RecordStatus.Active;
Record result = wr.Add(rec);
wr.SaveChanges();
newRecordId = result.ID;
}
}
}
When I am listing these records, I also want my page to display these users' usernames.
I get all the active records from the repository I created.
public ActionResult Index()
{
RecordListVMviewModel = new RecordListVM();
using (RecordRepository wr = new (RecordRepository())
{
viewModel.Records = wr.GetAll();
}
return View(viewModel);
}
And this is the repository code:
public class RecordRepository: Repository<Record>
{
public override List<Record> GetAll()
{
IQueryable<Record> activeRecords = DbSet.Where(w => w.Status == Enums.RecordStatus.Active);
return activeRecords.ToList();
}
}
Where do I have to make changes? Can you give me an sample code for usages like this?
Thank you.
You need to change
public string CreatingUserID { get; set; }
public string UpdatingUserID { get; set; }
to something like:
public User CreatingUser { get; set; }
public User UpdatingUser { get; set; }
Set the ID's during the creation of new RecordRepository()
Then access them as Record.CreatingUser.FirstName ect

Resources