Entity Framework - Updating relationship by changi

2020-06-03 07:58发布

问题:

I have the two following models and DbContext:

    public class TestDbContext : DbContext
    {
        public IDbSet<Person> People { get; set; }
        public IDbSet<Car> Cars { get; set; }
    }

    public class Person
    {
        public Person()
        {
            ID = Guid.NewGuid();
        }

        public Guid ID { get; set; }
        public string Name { get; set; }
        public virtual List<Car> Cars { get; set; }
    }

    public class Car
    {
        public Car()
        {
           ID = Guid.NewGuid();
        }

        public Guid ID { get; set; }
        public string Name { get; set; }
        public virtual Person Owner { get; set; }
    }

I then declare a list of people and a list of cars, setting the owner of the first car to the first person in the list:

List<Person> People = new List<Person>()
        {
            new Person() {Name = "bill", ID = new Guid("6F39CC2B-1A09-4E27-B803-1304AFDB23E3")},
            new Person() {Name = "ben", ID = new Guid("3EAE0303-39D9-4FD9-AF39-EC6DC73F630B")}
        };

        List<Car> Cars = new List<Car>() { new Car() { Name = "Ford", Owner = People[0], ID = new Guid("625FAB6B-1D56-4F57-8C98-F9346F1BBBE4") } };   

I save this off to the database using the following code and this works fine.

using (TestDbContext context = new TestDbContext())
        {
            foreach (Person person in People)
            {
                if (!(context.People.Any(p => p.ID == person.ID)))
                    context.People.Add(person);
                else
                {
                    context.People.Attach(person);
                    context.Entry<Person>(person).State = System.Data.EntityState.Modified;
                }
            }
            foreach (Car caar in Cars)
            {
                if (!(context.Cars.Any(c => c.ID == caar.ID)))
                    context.Cars.Add(caar);
                else
                {
                    context.Cars.Attach(caar);
                    context.Entry<Car>(caar).State = System.Data.EntityState.Modified;
                }
            }
            context.SaveChanges();
        }

If I then change the owner of the car to the second person and run the code again, the Car owner property doesn't get updated.

Cars[0].Owner = People[1];

Any ideas to what I'm doing wrong? Thanks for any help.

回答1:

I believe this is a problem of independent vs foreign key association. You are using independent association at the moment and the relation between car and person is actually managed by separate entry object which has its own state (to access this object you must use ObjectContext API). Setting the car's entity entry to modified state will not change the state of the entry for the relation! The simple solution is to use foreign key association instead which means adding new Guid PersonId property to your car and map it as foreign key property for Person navigation property.

If you insist on using independent associations you should change relations only on attached entities otherwise you will had a strong headache with tracking those changes and setting all required entries with correct state. It should be enough to create objects, attach them to context and only after that set the owner of the car - hopefully it will be tracked as a change.



回答2:

Try something like this :

    using (TestDbContext context = new TestDbContext())
            {
                foreach (Person person in People)
                {
                    if (!(context.People.Any(p => p.ID == person.ID)))
                        context.People.Add(person);
                    else
                    {
                        context.People.Attach(person);
                        context.Entry<Person>(person).State = System.Data.EntityState.Modified;
                    }
                    context.SaveChanges();
                }
                foreach (Car caar in Cars)
                {
                    if (!(context.Cars.Any(c => c.ID == caar.ID)))
                        context.Cars.Add(caar);
                    else
                    {
                        context.Cars.Attach(caar);
                        context.Entry<Car>(caar).State = System.Data.EntityState.Modified;
                    }
                    context.SaveChanges();
                }

            }

I think your error is due to the context.SaveChanges placements (and partialy your architecture). Consider using a dedicated method (a basic CRUD for instance) for each operation on your DB via Entity Framework. Hope this helps.

Edit : With a CRUD approch :

public class PersonManager // CRUD
    {
        public void Create(Person person)
        {
            using (TestDbContext context = new TestDbContext())
            {
                context.Person.Add(person);
                context.SaveChanges();
            }
        }

        public void Update(Person person)
        {
            using (TestDbContext context = new TestDbContext())
            {
                context.Person.Attach(person);
                context.Entry(person).State = System.Data.EntityState.Modified;
                context.SaveChanges();
            }
        }
    }

You could also make this class static in order to fit to your architecture.