【问题标题】:How to prevent referenced object's version column updating when creating a new object in EF 4.1 code first?首先在 EF 4.1 代码中创建新对象时如何防止引用对象的版本列更新?
【发布时间】:2011-12-21 12:10:26
【问题描述】:

我一直在尝试使用 EF 4.1 代码优先映射一对多、单向关系,例如用户有地址,但地址对用户一无所知。使用 ForeignKey 属性或 fluent api(显示在包含的代码中)很容易实现。

在两个映射类上添加带有 Timestamp 属性的 Version (byte[]) 列时会出现问题。如果我们现在创建一个引用现有(在数据库中)地址的用户实例并将其添加到上下文中,则在调用 SaveChanges 时,数据库分析器将显示两个数据库查询,一个是用户插入,另一个是是对地址表的更新以更改版本。不是我想要的。如果我在我的域中没有建模任何关系,那么我也不希望任何版本更改。如果我更改了 Address 实例,我只想更改 Address 的版本。

我怀疑由于映射使用HasMany(),EF DbContext 在内部认为存在一个需要满足的集合,并且随着集合已更改(通过添加新用户),它会自动更新地址的版本。尽管 Address 没有 ICollection<User> 类型的集合属性,但所有这一切。

所以我的问题。当我添加新用户时,我需要为关系设置什么映射以保持类结构不变而不更改地址版本?

编辑: 我发现,我可以阻止更新 Address 版本的唯一方法是将映射减少到 HasRequired(a => a.Address) 并且不再具有 User 类上的 AddressId 外键。如果外键“属性”在用户上,则显式映射或约定映射将确保地址的版本得到更新。

我更愿意对 HasRequired 应用一些扩展来告诉上下文如何处理关系,而不是必须完全删除外键属性。

这是我用来演示问题的代码:

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

namespace DbTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Address address = null;

            // Make sure we have one address to test with
            using (var context = new DemoContext())
            {
                address = context.Addresses.FirstOrDefault();
                if (address == null)
                {
                    address = new Address { Street = "My Street" };
                    context.Addresses.Add(address);
                    context.SaveChanges();
                }
            }

            byte[] version = address.Version;

            using (var context = new DemoContext())
            {
                // Uncomment to test attaching
                // context.Addresses.Attach(address);
                address = context.Addresses.FirstOrDefault();

                var user = new User { Name = "Mark", Address = address };
                context.Users.Add(user);
                context.SaveChanges(); // Results in new user inserted and a version update to the Address referenced object
            }

            using (var context = new DemoContext())
            {
                var address2 = context.Addresses.FirstOrDefault();
                Console.WriteLine("Versions: {0}, {1}", BitConverter.ToString(version), BitConverter.ToString(address2.Version));
            }

            Console.ReadLine();
        }
    }

    public class User
    {
        [Key]
        public int UserId { get; set; }
        public string Name { get; set; }
        public int AddressId { get; set; }

//        [ForeignKey("AddressId")]
        public Address Address { get; set; }

        [Timestamp]
        public byte[] Version { get; set; }
    }

    public class Address
    {
        [Key]
        public int AddressId { get; set; }
        public string Street { get; set; }
        [Timestamp]
        public byte[] Version { get; set; }
    }

    public class DemoContext : DbContext
    {
        public DbSet<User> Users { get; set; }
        public DbSet<Address> Addresses { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>()
                        .HasRequired(a => a.Address)
                        .WithMany()
                        .HasForeignKey(u => u.AddressId);
        }
    }
}

【问题讨论】:

    标签: entity-framework-4


    【解决方案1】:

    解决方法:

    利用您公开的外键并替换此代码...

    address = context.Addresses.FirstOrDefault();
    var user = new User { Name = "Mark", Address = address };
    

    ...作者:

    var addressId = context.Addresses.Select(a => a.AddressId).FirstOrDefault();
    var user = new User { Name = "Mark", AddressId = addressId };
    

    这不会改变地址实体的时间戳。

    我认为没有任何映射选项可以避免原始代码中地址的 UPDATE 语句。我会遵循您的假设,即 EF 将您的代码视为用户和地址之间关系的更改,因此会更新地址,无论用户集合是否在 Address 模型中公开。

    【讨论】:

    • 虽然有效,但它仅在地址尚未在上下文中时才有用。如果它在上下文中,版本将被更新。
    • ...所以检查它并首先从上下文中分离地址。
    【解决方案2】:

    我发现阻止更新 Address 版本的唯一方法是将映射减少到仅 HasRequired(a =&gt; a.Address) 并且不再在 User 类上具有 AddressId 外键。似乎如果外键“属性”在用户上,则显式映射或约定将确保地址的版本得到更新。

    我更愿意对 HasRequired 应用一些扩展来告诉上下文如何处理关系,而不是必须完全删除外键属性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-02
      • 1970-01-01
      • 1970-01-01
      • 2019-09-10
      • 1970-01-01
      • 1970-01-01
      • 2011-10-06
      • 1970-01-01
      相关资源
      最近更新 更多