简体   繁体   中英

ASP .NET One-to-One Relationship CRUD

There's a problem with Edit action autogenerated with controller on the basis of a code-first class UserProfile .

public class UserProfile : ApplicationUser
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
        public string ProfilePhoto { get; set; }
        public string Interests { get; set; }
        public string AboutMe { get; set; }

        public Address Address { get; set; }
        public List<Post> Posts { get; set; }
        public List<Friends> Friends { get; set; }
        public List<Messages> Messages { get; set; }
        public List<UsersGallery> UsersGallery { get; set; }
    }

UserProfile is in one-to-one relationship with class Address .

public class Address
    {
        [Key]
        public string AddressId { get; set; }

        public string City { get; set; }
        public string Street { get; set; }
        public string HouseOrFlatNumber { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }

        public string UserProfileForeignKey { get; set; }
        public UserProfile UserProfile { get; set; }
    }

The relationship is described in FluentApi as follows:

protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            builder.Entity<UserProfile>()
            .HasOne(p => p.Address)
            .WithOne(i => i.UserProfile)
            .HasForeignKey<Address>(b => b.UserProfileForeignKey);
        }

The UserProfile and Address entities are created in Register action

public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
        {
            ViewData["ReturnUrl"] = returnUrl;
            if (ModelState.IsValid)
            {
                var user = new UserProfile { UserName = model.FirstName, Email = model.Email, FirstName = model.FirstName, LastName = model.LastName, Address = new Address() };
                var result = await _userManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
                    await _emailSender.SendEmailConfirmationAsync(model.Email, callbackUrl);

                    await _signInManager.SignInAsync(user, isPersistent: false);
                    _logger.LogInformation("User created a new account with password.");
                    return RedirectToLocal(returnUrl);
                }
                AddErrors(result);
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

At this point I have the appropriate entries in the database tables related by the UserProfileForeignKey. - the UserProfile table the UserProfile table - the Address table the Address table

And here appears a problem. According to the sql error message, the autogenerated CRUD action Edit instead of altering the entry in Address table tries to add a new entry with the same UserProfileForeignKey.

Do I understand it correctly? Why is that so and what can be done to make the Edit action work as it should?

[HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(string id, [Bind("FirstName,LastName,BirthDate,ProfilePhoto,Interests,AboutMe,Address,Id,UserName,NormalizedUserName,Email,NormalizedEmail,EmailConfirmed,PasswordHash,SecurityStamp,ConcurrencyStamp,PhoneNumber,PhoneNumberConfirmed,TwoFactorEnabled,LockoutEnd,LockoutEnabled,AccessFailedCount")] UserProfile userProfile)
        {
            if (id != userProfile.Id)
            {
                return NotFound();
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(userProfile);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!UserProfileExists(userProfile.Id))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(userProfile);
        }

Here is the sql error message:

An unhandled exception occurred while processing the request.

SqlException: Cannot insert duplicate key row in object 'dbo.Address' with unique index 'IX_Address_UserProfileForeignKey'.

The duplicate key value is (9872561e-dad4-4169-9faf-154c7dcd925f). The statement has been terminated.

System.Data.SqlClient.SqlCommand+<>c.b__108_0(Task result) DbUpdateException: An error occurred while updating the entries. See the inner exception for details.

Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch+d__32.MoveNext()

It says the ForeignKey 9872561e-dad4-4169-9faf-154c7dcd925f is tried to be duplicated but why if it is an update not an insert?

This is because you UserProfile entity is detached (entity framework is not tracking it), so it sets entity.State == Added here. To attach your entity please see How to Update Existing Disconnected Entity

Edit 27/01/2018 I tried to replicate your scenario and have created a project in GitHub https://github.com/updateaman/EF6-Test

After setting/passing the UserProfileForeignKey property inside Address object, I was able to get rid of this error

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM