代码优先导航属性正在保留但未加载

本文关键字:保留 加载 导航 属性 代码 | 更新日期: 2023-09-27 18:31:44

我一直在开发一个数据库的框架,该数据库将支持使用 EF 4.3.1 中的 Code First 对数据进行版本控制。

几天前,我让模型持续存在并正确加载,但从那以后我破坏了一些东西,我无法弄清楚出了什么问题。所有类都被映射并创建表,数据也被持久化。所以在存储方向上,一切正常!但是,当我尝试加载Registration实体时,这些值都是默认构造函数设置它们的值。我在想也许在调用 Registration 构造函数后没有加载数据,但我目前的能力已经到了弄清楚发生了什么的尽头!

基础是这两个类,我的可验证类是从这两个类派生出来的......

public abstract class VersionBase<T> {
    [Key]
    public Int64 Id { get; protected set; }
    public DateTime CreationDateTime { get; protected set; }
    // Value is virtual to support overriding to let deriving classes specify attributes for the property, such as [Required] to specify a non-nullable System.String
    public virtual T Value { get; internal set; }
    protected VersionBase() {
        CreationDateTime = DateTime.Now;
    }
    protected VersionBase(T value)
        : this() {
        Value = value;
    }
}
public abstract class VersionedBase<TVersion, TBase>
    where TVersion : VersionBase<TBase>, new() {
    [Key]
    public Int64 Id { get; protected set; }
    public virtual ICollection<TVersion> Versions { get; protected set; }
    protected VersionedBase() {
        Versions = new List<TVersion>();
    }
    [NotMapped]
    public Boolean HasValue {
        get {
            return Versions.Any();
        }
    }
    [NotMapped]
    public TBase Value {
        get {
            if (HasValue)
                return Versions.OrderByDescending(x => x.CreationDateTime).First().Value;
            throw new InvalidOperationException(this.GetType().Name + " has no value");
        }
        set {
            Versions.Add(new TVersion { Value = value });
        }
    }
}

派生类的示例...

public class VersionedInt32 : VersionedBase<VersionedInt32Version, Int32> { }
public class VersionedInt32Version : VersionBase<Int32> {
    public VersionedInt32Version() : base() { }
    public VersionedInt32Version(Int32 value) : base(value) { }
    public static implicit operator VersionedInt32Version(Int32 value) {
        return new VersionedInt32Version { Value = value };
    }
}

。和。。。

public class VersionedString : VersionedBase<VersionedStringVersion, String> { }
public class VersionedStringVersion : VersionBase<String> {
    public VersionedStringVersion() : base() { }
    public VersionedStringVersion(String value) : base(value) { }
    public static implicit operator VersionedStringVersion(String value) {
        return new VersionedStringVersion { Value = value };
    }
    /// <summary>
    /// The [Required] attribute tells Entity Framework that we want this column to be non-nullable
    /// </summary>
    [Required]
    public override String Value { get; internal set; }
}

我的调用代码是这样的...

static void Main(String[] args) {
    using (var db = new VersionedFieldsContext()) {
        Registration registration = new Registration();
        registration.FirstName.Value = "Test";
        registration.FirstName.Versions.Add("Derp");
        db.Registration.Add(registration);
        db.SaveChanges();
    }
    using (var db = new VersionedFieldsContext()) {
        Registration registration = db.Registration.First();
        // InvalidOperationException at next line: "VersionedString has no value"
        String asdf = registration.FirstName.Value;
    }
}
public class Registration {
    [Key]
    public Int64 Id { get; set; }
    public DateTime CreationDateTime { get; set; }
    public VersionedString FirstName { get; set; }
    public Registration() {
        CreationDateTime = DateTime.Now;
        FirstName = new VersionedString();
    }
}
public class VersionedFieldsContext : DbContext {
    public DbSet<Registration> Registration { get; set; }
    public VersionedFieldsContext() {
        Database.SetInitializer<VersionedFieldsContext>(new DropCreateDatabaseIfModelChanges<VersionedFieldsContext>());
    }
    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }
}

感谢您的任何见解!

代码优先导航属性正在保留但未加载

您需要进行两项更改:

  • Registration构造函数中删除FirstName的实例化,以便构造函数仅:

    public Registration() {
        CreationDateTime = DateTime.Now;
    }
    

    在实体的默认构造函数中创建导航引用(而不是集合)的实例会导致已知问题:什么会导致实体框架将卸载(但延迟可加载)引用保存在现有数据上?

  • 如果已修复第一个点,则自定义异常将更改为NullReferenceException。要解决此问题,请在 Registration virtual 中创建 FirstName 属性,因为第二个 using 块中的代码需要延迟加载:

    public virtual VersionedString FirstName { get; set; }
    

编辑

创建注册并自动实例化FirstName的解决方法可能是工厂方法:

public class Registration {
    [Key]
    public Int64 Id { get; set; }
    public DateTime CreationDateTime { get; set; }
    public VersionedString FirstName { get; set; }
    public Registration() {
        CreationDateTime = DateTime.Now;
    }
    public static Registration Create() {
        return new Registration {
            FirstName = new VersionedString()
        }
    }
}

EF 在具体化 Registration 对象时使用默认构造函数。在自定义代码中,当您需要创建 Registration 的实例时,可以使用 factory 方法:

var registration = Registration.Create();

但是,当您使用更改跟踪或延迟加载代理并希望手动创建代理实例时,它可能不太有用:

var registration = db.Registration.Create();

这将再次调用默认构造函数,您必须在创建对象后实例化FirstName