EF 4.1 Code First - изменение значения FK не обновляется при использовании нового контекста / над WCF - PullRequest
4 голосов
/ 20 апреля 2011

Я использую EF Code First поверх WCF. Поэтому, когда я сохраняю сущность, она использует новый контекст.

Если я получаю сущность, а затем обновляю ее, чтобы она ссылалась на другую сущность, я обнаружил, что она сохраняется с исходным значением внешнего ключа.

Например, я получаю класс компании, где страна - Великобритания. Я тогда изменяю это в США и передаю это сервису. Когда я проверяю таблицу, FK по-прежнему установлен на британский.

Как я могу обновить внешний ключ?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity.ModelConfiguration;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Data;

namespace CodeFirstExistingDatabase
{

    class Program
    {
        private const string ConnectionString = @"Server=.\sql2005;Database=CodeFirst2;integrated security=SSPI;";

        static void Main(string[] args)
        {

            // Firstly, create a new country record.
            Country country = new Country();
            country.Code = "UK";
            country.Name = "United Kingdom";

            // Create aother new country record.
            Country country2 = new Country();
            country2.Code = "USA";
            country2.Name = "US of A";

            // Now create an instance of the context.
            MyContext myContext = new MyContext(ConnectionString);
            myContext.Entry(country).State = EntityState.Added;
            myContext.Entry(country2).State = EntityState.Added;
            myContext.SaveChanges();
            Console.WriteLine("Saved Countries");

            // Now insert a Company record
            Company company = new Company();
            company.CompanyName = "AccessUK";
            company.HomeCountry = myContext.Countries.First(e => e.Code == "UK");
            myContext.Companies.Add(company);
            myContext.SaveChanges();
            Console.WriteLine("Saved Company");

            Company savedCompany = myContext.Companies.First(e => e.CompanyName == "AccessUK");
            Country usCountry = myContext.Countries.First(e => e.Code == "USA");
            savedCompany.HomeCountry = usCountry;

            // Create another context for the save (as if we're passing the entity back over WCF and thus
            // creating a new context in the service)
            MyContext myContext2 = new MyContext(ConnectionString);
            myContext2.Entry(savedCompany).State = EntityState.Modified;
            myContext2.Entry(savedCompany.HomeCountry).State = EntityState.Modified;
            myContext2.SaveChanges();

            // When I check the company table, it has the foreign key of the UK Country.  It should have 
            // that of USA.

            Console.WriteLine("Finished");
            Console.ReadLine();

        }
    }

        public class MyContext
            : DbContext
        {
            public DbSet<Company> Companies { get; set; }
            public DbSet<Country> Countries { get; set; }

            public MyContext(string connectionString)
                : base(connectionString)
            {
            }
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Configurations.Add(new CountryConfiguration());
                modelBuilder.Configurations.Add(new CompanyConfiguration());

                base.OnModelCreating(modelBuilder);
            }
        }

    public class CompanyConfiguration
        : EntityTypeConfiguration<Company>
    {

        public CompanyConfiguration()
            : base()
        {

            HasKey(p => p.Id);
            Property(p => p.Id)
                .HasColumnName("Id")
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
                .IsRequired();

            Property(p => p.CompanyName)
                .HasColumnName("Name")
                .IsRequired();
            HasRequired(x => x.HomeCountry).WithMany()
                .Map(x => x.MapKey("HomeCountryId"));

            ToTable("Companies");
        }

    }

    public class CountryConfiguration
        : EntityTypeConfiguration<Country>
    {

        /// <summary>
        /// Initializes a new instance of the <see cref="CountryConfiguration"/> class.
        /// </summary>
        public CountryConfiguration()
            : base()
        {

            HasKey(p => p.Id);
            Property(p => p.Id)
                .HasColumnName("Id")
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
                .IsRequired();
            Property(p => p.Code)
                .HasColumnName("Code")
                .IsRequired();
            Property(p => p.Name)
                .HasColumnName("Name")
                .IsRequired();

            ToTable("Countries");
        }

    }

    public class Company
    {
        public int Id { get; set; }
        public string CompanyName { get; set; }
        public Country HomeCountry { get; set; }
    }

    public class Country
    {
        public int Id { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
    }
}

Большое спасибо,

Paul.

1 Ответ

2 голосов
/ 20 апреля 2011

Это хорошо известная проблема обособленных субъектов и независимых ассоциаций. Проблема состоит в том, что установка объекта в модифицированное не устанавливает отношения для модифицированного. Подробное описание всех проблем здесь . Ответ связан с EFv4 и ObjectContext API, но EFv4.1 - всего лишь обертка вокруг него, поэтому смысл тот же.

Источником этой проблемы является то, что если вы используете отдельные сущности, вы должны сказать EF, что отношение изменилось. EF не будет делать никаких предположений для вас. Проблема с DbContext API еще хуже, потому что DbContext API не предлагает методов для изменения состояния независимой ассоциации , поэтому вы должны вернуться к ObjectContext API и использовать ObjectStateManager.ChangeRelationshipState.

Существуют и другие способы, как справиться с этим, если вы не используете удаленные вызовы в качестве WCF - это просто порядок команд, который вы должны выполнить, чтобы сделать эту работу, но при использовании WCF вы не можете присоединить объект, прежде чем установить отношение и т.д. Хорошо, теоретически вы можете, но это полностью изменит ваш контракт на обслуживание и хореографию обмена сообщениями.

Таким образом, как правило, самое простое решение в этом сценарии - использовать сопоставление внешнего ключа вместо независимого сопоставления . Ассоциация внешнего ключа не отслеживается как отдельный объект с состоянием (как независимая ассоциация), поэтому изменение состояния объекта на измененное будет просто работать.

public class Company
{
    public int Id { get; set; }
    public string CompanyName { get; set; }
    public int HomeCountryId { get; set; }
    public Country HomeCountry { get; set; }
}


HasRequired(x => x.HomeCountry)
    .WithMany()
    .HasForeignKey(x => x.HomeCountryId);

Любое другое решение заключается в том, чтобы сначала загрузить старый объект из БД и объединить изменения в присоединенный объект - это то, что я делаю все время, и вам придется делать это, как только вы начнете работать с отношениями "многие ко многим" или удаление в отношениях один-ко-многим.

...