NHibernate: Cannot assign property value in entity's constructor
When I run the following code, at B's constructor, I got a NullReferenceException. I cannot assign value to B's Number property.
Models:
public class A
{
public A()
{
this.Number = 1;
if (this.Number != 1)
{
throw new Exception("Failed to assign value in A's constructor");
}
}
public virtual int Id { get; private set; }
public virtual int Number { get; set; }
public virtual B B { get; set; }
}
public class B
{
public B()
{
this.Number = 1;
// Throws NullReferenceException: Object reference not set to an instance of an object.
if (this.Number != 1)
{
throw new Exception("Failed to assign value in B's constructor");
}
}
public virtual int Id { get; private set; }
public virtual int Number { get; set; }
}
Mappings:
public class AMappings : ClassMap<A>
{
public AMappings()
{
Id(x => x.Id);
Map(x => x.Number);
References(x => x.B).Cascade.All();
}
}
public class BMappings : ClassMap<B>
{
public BMappings()
{
Id(x => x.Id);
Map(x => x.Number);
}
}
Main method:
class Program
{
static void Main(string[] args)
{
// Create connection string
string connectionString = new System.Data.SqlClient.SqlConnectionStringBuilder()
{
DataSource = @".\r2",
InitialCatalog = "TestNHibernateMappings",
IntegratedSecurity = true
}.ConnectionString;
// Create SessionFactory
ISessionFactory sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration
.MsSql2008.ConnectionString(connectionString)
.ShowSql())
.Mappings(m => m.FluentMappings
.Add(typeof(AMappings)开发者_开发技巧)
.Add(typeof(BMappings)))
.ExposeConfiguration(BuildSchema)
.BuildConfiguration()
.BuildSessionFactory();
// Create test object in DB
using (var session = sessionFactory.OpenSession())
{
using (var trans = session.BeginTransaction())
{
var a = new A();
a.B = new B();
session.Save(a);
trans.Commit();
}
}
// Read test object from DB
using (var session = sessionFactory.OpenSession())
{
using (var trans = session.BeginTransaction())
{
var a = session.Get<A>(1);
}
}
}
static void BuildSchema(Configuration cfg)
{
new SchemaExport(cfg).Create(false, true);
}
}
I'm using NHibernate 3.1.0.4000, FluentNHibernate 1.2.0.712.
Any ideas? Thanks.
The key here is that Number
is virtual.
A.B
is being lazy-loaded. NHibernate creates a proxy for B
which overrides each virtual property in the class. The first time one of the non-Id
properties is accessed, NHibernate will load the data from the database to populate the object.
Since this proxy class is a subclass of B
, B
's constructor will be called before the proxy constructor. When B
's constructor sets the virtual property Number
, it is calling the Number
property as defined in the proxy subclass, which has not yet been initialized.
For a more thorough discussion of constructors and inheritance, see http://www.yoda.arachsys.com/csharp/constructors.html
To fix this, convert any properties you wish to set in the constructor to use backing fields instead of auto-properties, then set the field instead of the property in the constructor.
public class B
{
public B()
{
_number = 1;
}
public virtual int Id { get; private set; }
private int _number;
public virtual int Number
{
get { return _number; }
set { _number = value; }
}
}
It's a bit more verbose, but it effectively avoids touching virtual methods or properties in the constructor.
精彩评论