我一直在开发数据库的骨架,该数据库支持使用EF 4.3.1中的Code First对数据进行版本控制。
几天前我让模型坚持并正确加载,但我已经破坏了一些东西,我无法弄清楚出了什么问题。所有类都被映射并创建表,数据也被持久化。所以在storring方向,一切正常!但是,当我尝试加载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>();
}
}
感谢您的任何见解!
答案 0 :(得分:2)
您需要进行两项更改:
从FirstName
构造函数中删除Registration
的实例化,以便构造函数只是:
public Registration() {
CreationDateTime = DateTime.Now;
}
在实体的默认构造函数中创建导航引用(不是集合)的实例会导致已知问题:What would cause the Entity Framework to save an unloaded (but lazy loadable) reference over existing data?
如果您已修复第一个点,则您的自定义例外会更改为NullReferenceException
。要解决此问题,请在FirstName
Registration
中生成virtual
属性,因为第二个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
的实例时使用工厂方法:
var registration = Registration.Create();
当您使用更改跟踪或延迟加载代理并希望手动创建代理实例时,它可能没那么有用:
var registration = db.Registration.Create();
这将再次调用默认构造函数,并且必须在已创建对象后实例化FirstName
。