问题
I have one-to-one relationship defined in EF6 which works for Inserts. Unfortunately when I try an update with a disconnected record, I receive an error. Here are details:
.NET Source:
namespace EF_ConsoleApp_Test
{
public class Program
{
public static void Main(string[] args)
{
int? accountId;
int? customerId;
using (var db = new MainContext())
{
var account = new Account
{
AccountNumber = "1234",
Customer = new Customer {FirstName = "John"}
};
db.Accounts.Add(account);
db.SaveChanges();
accountId = account.Id;
customerId = account.Customer.Id;
}
using (var db = new MainContext())
{
// disconnected record
var account = new Account()
{
Id = accountId,
AccountNumber = "9876",
Customer = new Customer() {Id = customerId}
};
db.Accounts.Add(account);
db.Entry(account).State = EntityState.Modified;
db.Entry(account.Customer).State = EntityState.Unchanged;
db.SaveChanges(); // Error occurs here
}
}
[Serializable]
[Table("CUSTOMERS")]
public class Customer
{
[Key] [Column("CUSTOMER_ID")] public int? Id { get; set; }
[Required]
[Column("FIRST_NAME")]
[StringLength(45)]
public string FirstName { get; set; }
public virtual Account Account { get; set; }
public Customer() { }
}
[Serializable]
[Table("ACCOUNTS")]
public class Account
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
[Column("ACCOUNT_ID")]
public int? Id { get; set; }
[Required]
[Column("ACCOUNT_NUMBER")]
[Display(Name = "Account Number")]
[StringLength(16)]
public string AccountNumber { get; set; }
public virtual Customer Customer { get; set; }
/// <summary>
/// Default Constructor
/// </summary>
public Account() { }
}
internal class MainContext : DbContext
{
internal MainContext() : base("name=ACHRE.Context")
{
Database.SetInitializer<MainContext>(null);
}
public virtual DbSet<Account> Accounts { get; set; }
public virtual DbSet<Customer> Customers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure FK
modelBuilder.Entity<Customer>()
.HasRequired(c => c.Account)
.WithRequiredPrincipal(a => a.Customer)
.Map(m => m.MapKey("CUSTOMER_ID"));
base.OnModelCreating(modelBuilder);
}
}
}
}
Database Table Create statements:
CREATE TABLE [dbo].[CUSTOMERS](
[CUSTOMER_ID] [INT] IDENTITY(1,1) NOT NULL,
[FIRST_NAME] [varchar](45) NOT NULL,
CONSTRAINT [PK_CUSTOMERS] PRIMARY KEY CLUSTERED
(
[CUSTOMER_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[ACCOUNTS](
[ACCOUNT_ID] [INT] IDENTITY(1,1) NOT NULL,
[CUSTOMER_ID] [int] NOT NULL,
[ACCOUNT_NUMBER] [varchar](16) NOT NULL,
CONSTRAINT [PK_ACCOUNTS] PRIMARY KEY CLUSTERED
(
[ACCOUNT_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[ACCOUNTS] WITH CHECK ADD CONSTRAINT [FK_ACCOUNTS_CUSTOMERS] FOREIGN KEY([CUSTOMER_ID])
REFERENCES [dbo].[CUSTOMERS] ([CUSTOMER_ID])
GO
Error:
A relationship from the 'Customer_Account' AssociationSet is in the 'Added' state. Given multiplicity constraints, a corresponding 'Customer_Account_Target' must also in the 'Added' state.
What do I need to update to make this work?
Notes
- This related to question I asked before that was related to Inserts. The Insert was resolved but introduced this issue when the Foreign Keys were removed.
- Using EF 6.2 and .NET 4.7.1.
回答1:
This is because you are adding the entity again. You need to attach the object first by calling db.Accounts.Attach(account);
.
Or a better approach would be to first fetch based on Id and then modify the desired fields like this :
using (var db = new MainContext())
{
var account = db.Accounts.SingleOrDefault(x => x.id == accountId);
account.AccountNumber = "9876"
db.SaveChanges();
}
回答2:
This is a side effect of the db.Accounts.Add(account);
call. It sets some shadow state to Added
which cannot be negated by the next State
manipulations.
Attach
in advance doesn't work in this case. So either use the second suggestion by @Harsh, or if you want to do forced update, don't call Add
but simply set the State
of the account
to Modified
. This will attach it and mark it as modified w/o affecting the associated Customer
.
using (var db = new MainContext())
{
// disconnected record
var account = new Account()
{
Id = accountId,
AccountNumber = "9876",
Customer = new Customer() {Id = customerId}
};
db.Entry(account).State = EntityState.Modified; // <-- enough
db.SaveChanges();
}
来源:https://stackoverflow.com/questions/50141979/ef-one-to-one-update-fails