NHibernate: невозможно присвоить значение свойства в конструкторе объекта - PullRequest
0 голосов
/ 26 апреля 2011

Когда я запускаю следующий код в конструкторе B, я получаю исключение NullReferenceException. Я не могу присвоить значение свойству B числа.

Модель:

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

Отображения:

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

Основной метод:

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

Я использую NHibernate 3.1.0.4000, FluentNHibernate 1.2.0.712.

Есть идеи? Спасибо.

1 Ответ

3 голосов
/ 26 апреля 2011

Ключевым моментом здесь является то, что Number является виртуальным.

A.B загружается ленивым. NHibernate создает прокси для B, который переопределяет каждое виртуальное свойство в классе. При первом обращении к одному из свойств, отличных от Id, NHibernate загрузит данные из базы данных для заполнения объекта.

Поскольку этот прокси-класс является подклассом B, конструктор B будет вызываться перед прокси-конструктором. Когда конструктор B устанавливает виртуальное свойство Number, он вызывает свойство Number, как определено в подклассе прокси, который еще не был инициализирован.

Более подробное обсуждение конструкторов и наследования см. В http://www.yoda.arachsys.com/csharp/constructors.html

Чтобы исправить это, преобразуйте любые свойства, которые вы хотите установить в конструкторе, чтобы использовать вспомогательные поля вместо авто-свойств, затем установите поле вместо свойства в конструкторе.

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

Это немного более многословно, но эффективно избегает касания виртуальных методов или свойств в конструкторе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...