【问题标题】:EF 4.1 Code First - change to FK value not updated when using new context/over WCFEF 4.1 Code First - 使用新上下文/通过 WCF 时未更新对 FK 值的更改
【发布时间】:2011-04-20 10:45:45
【问题描述】:

我在 WCF 上使用 EF Code First。所以当我保存一个实体时,它正在使用一个新的上下文。

如果我检索一个实体,然后更新它以使其引用不同的实体,我发现它与原始外键值一起保存。

例如,我检索了一个 Company 类,其中国家/地区是 UK。然后我将其更改为美国并将其传递回服务。当我检查表格时,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; }
    }
}

非常感谢,

保罗。

【问题讨论】:

    标签: wcf entity-framework entity-relationship ef-code-first entity-framework-4.1


    【解决方案1】:

    这是众所周知的分离实体和独立关联的问题。问题在于将实体设置为已修改不会将关系设置为已修改。关于所有相关问题的详细描述is here。答案与 EFv4 和 ObjectContext API 有关,但 EFv4.1 只是它的一个包装器,因此含义相同。

    这个问题的根源是,如果你使用分离的实体,你必须说 EF 关系已经改变。 EF 不会为您做任何假设。 DbContext API 的问题更严重,因为DbContext API doesn't offer methods to change state of independent association 所以你必须恢复到 ObjectContext API 并使用ObjectStateManager.ChangeRelationshipState

    如果您不将远程调用用作 WCF,还有其他方法可以处理它 - 这只是您必须执行的命令顺序才能使其工作,但是在使用 WCF 时,您无法在设置之前附加实体关系等。理论上你可以,但它会完全改变你的服务合同和消息编排。

    因此,在这种情况下,通常最简单的解决方案是使用foreign key association instead of independent association。外键关联不作为具有状态的单独对象(作为独立关联)进行跟踪,因此将实体的状态更改为已修改将正常工作。

    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);
    

    任何其他解决方案都是先从数据库加载旧对象并将更改合并到附加对象中 - 这就是我一直在做的事情,一旦你开始处理多对多关系或你将不得不这样做在一对多关系中删除。

    【讨论】:

    • 关于使用 HomeCountryId 属性是否意味着在将 Country 实例分配给 Company 时,我还需要手动分配 HomeCountryId 值?这是我发现我可以让这种方法发挥作用的唯一方法。谢谢。
    • @Paul:是的,实际上设置 HomeCountryId 就足够了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多