views:

180

answers:

3

I have a class, Task, and it has a property TaskLibrary which is dll it will load and run some code from. So, any task has one library, but any library can have many tasks. My problem is that my test for making sure the task's Library property is not null is failing (so it could just be my test). My classes are effectively this:

public class Task
{
  public virtual int TaskId {get;set;}
  public virtual string Locked {get;set;}
  public virtual int Status {get;set;}
  public virtual TaskLibrary Library {get;set;}
}

public class TaskLibrary
{
  public virtual int LibraryId {get;set}
  public virtual string Name {get;set;}
  public virtual string Description {get;set;}
  public virtual byte[] Dll {get;set}
  public virtual IEnumerable<Task> Tasks {get;set;}
}

My NHibernate mappings look like this:

  <class name="Task">
    <id name="Id" column="TaskId" type="Int32" unsaved-value="-1">
      <generator class="identity"/>
    </id>
    <property name="Locked" column="Locked"/>
    <property name="Status" column="Status"/>
    <many-to-one name="Library" class="TaskLibrary" fetch="join"/>
  </class>
  <class name="TaskLibrary">
    <id name="Id" column="LibraryId">
      <generator class="identity"/>
    </id>
    <property name="Name"/>
    <property name="Description"/>
    <property name="Dll"/>
    <set name="Tasks" lazy="true">
      <key column="LibraryId"/>
      <one-to-many class="Task"/>
    </set>
  </class>

My test class looks like this:

[TestFixture]
public class TaskRepositoryFixture
{
    private ISessionFactory _sessionFactory;
    private Configuration _configuration;

    private readonly Task[] _tasks = new[]
        {
            new Task {Id = 1, Status = 1, Locked = 0, Library = new TaskLibrary { Id =1, Description = "Test Library", Name = "Tast.dll", Type = "RunnableTask", Dll = Encoding.ASCII.GetBytes("test binary data")}},
            new Task {Id = 2, Status = 1, Locked = 0, Library = new TaskLibrary { Id =1, Description = "Test Library", Name = "Tast.dll", Type = "RunnableTask", Dll = Encoding.ASCII.GetBytes("test binary data")}},
            new Task {Id = 3, Status = 1, Locked = 0, Library = new TaskLibrary { Id =2, Description = "Test Library 2", Name = "Tast2.dll", Type = "RunnableTask", Dll = Encoding.ASCII.GetBytes("test binary data")}},
            new Task {Id = 4, Status = 1, Locked = 0, Library = new TaskLibrary { Id =2, Description = "Test Library 2", Name = "Tast2.dll", Type = "RunnableTask", Dll = Encoding.ASCII.GetBytes("test binary data")}},
            new Task {Id = 5, Status = 1, Locked = 0, Library = new TaskLibrary { Id =3, Description = "Test Library 3", Name = "Tast3.dll", Type = "RunnableTask", Dll = Encoding.ASCII.GetBytes("test binary data")}},
        };

    private readonly TaskLibrary[] _libraries = new[]
        {
            new TaskLibrary { Id =1, Description = "Test Library", Name = "Tast.dll", Type = "RunnableTask", BinaryDll = Encoding.ASCII.GetBytes("test binary data")},
            new TaskLibrary { Id =2, Description = "Test Library 2", Name = "Tast2.dll", Type = "RunnableTask", BinaryDll = Encoding.ASCII.GetBytes("test binary data")},
            new TaskLibrary { Id =3, Description = "Test Library 3", Name = "Tast3.dll", Type = "RunnableTask", BinaryDll = Encoding.ASCII.GetBytes("test binary data")}
        };

    private void CreateInitialData()
    {
        using (ISession session = _sessionFactory.OpenSession())
        using (ITransaction transaction = session.BeginTransaction())
        {
            foreach (var lib in _libraries)
                session.Save(lib);

            foreach (var task in _tasks)
                session.Save(task);

            transaction.Commit();
        }
    }


    [TestFixtureSetUp]
    public void TestFixtureSetUp()
    {
        _configuration = new Configuration();
        _configuration.Configure();
        _configuration.AddAssembly("DistPollAutoTasksShared");
        _sessionFactory = _configuration.BuildSessionFactory();
    }

    [SetUp]
    public void SetupContext()
    {
        new SchemaExport(_configuration).Execute(false, true, false, false);
        CreateInitialData();
    }

    [Test]
    public void CanGetLibraryFromTask()
    {
        ITaskRepository repository = new TaskRepository();
        var fromDb = repository.GetById(_tasks[0].Id);
        Assert.IsNotNull(fromDb.Library);
        Assert.IsNotNull(fromDb.Library.Dll);
    }
  }

And, the Tasks table in the MSSQL2000 database is this:

CREATE TABLE [dbo].[Tasks](
    [TaskId] [int] IDENTITY(1,1) NOT NULL,
    [TaskLibrary] [int] NOT NULL,
    [Status] [int] NOT NULL,
    [Locked] [int] NOT NULL
)

If you're still with me...

From my Task class, I just want an instance of the TaskLibrary class for the Library property. Also, if I'm working with the libraries themselves, I want to be able to lazily retrieve an IEnumerable of all tasks using that library. However, when I run the test, I get this error:

TestCase 'DistPollAutoTasksShared.Tests.TaskRepositoryFixture.CanGetLibraryFromTask'
failed: NHibernate.LazyInitializationException : Could not initialize proxy - no Session.
    at NHibernate.Proxy.AbstractLazyInitializer.Initialize()
    at NHibernate.Proxy.AbstractLazyInitializer.GetImplementation()
    at NHibernate.Proxy.Poco.Castle.CastleLazyInitializer.Intercept(IInvocation invocation)
    at Castle.DynamicProxy.AbstractInvocation.Proceed()
    at TaskLibraryProxy2bd44073e90f47298039abfbfda11492.get_Dll()

This is the first time I've used NHibernate, so I'm still learning. I really want to get a good foundation of the basics, so I'm stuck here until then. Any help, suggestions, reading material (I've read all of this question's suggestions and some others) would be appreciated.

EDIT:

After changing fetch="join", I'm getting the functionality I want from the Task class. However, I've added another test for the Tasks property of the TaskLibrary class:

    [Test]
    public void CanGetTasksByLibrary()
    {
        ITaskLibraryRepository repository = new TaskLibraryRepository();
        var fromDb = repository.GetById(_libraries[0].Id).Tasks;

        Assert.IsNotNull(fromDb);
        Assert.True(fromDb.Count() == 2, "Cannot get IEnumerable<Task> from TaskLibrary");
    }

But, an assertion fails with this error (I've updated the code above to reflect any changes I've made):

TestCase 'DistPollAutoTasksShared.Tests.TaskLibraryRepositoryFixture.CanGetTasksByLibrary'
failed: 
  Cannot get IEnumerable<Tasks> from TaskLibrary
  Expected: True
  But was:  False
A: 

did you try setting Lazy="false" in your mapping? http://code-redefined.blogspot.com/2007/07/hibernate-lazy-fetch-problems-could-not.html

Chris Conway
Also check the unit of work you're dealing with... if each access is wrapped in it's own transaction, you could be getting the No Session error. See this for an example: http://stackoverflow.com/questions/345705/hibernate-lazyinitializationexception-could-not-initialize-proxy
Chris Conway
I did just now, but I get the same exception. Although, I do want lazy loading of Tasks from the TaskLibrary class.
scottm
+1  A: 
<many-to-one name="Library" class="TaskLibrary" fetch="join" />

This would join the Library on every select.

<many-to-one name="Library" class="TaskLibrary" lazy="false" />

This will eagerly execute separate select for the Library.

Otherwise, it will lazy load the Library if you only set fetch="select" (which is the default).

http://ayende.com/Blog/archive/2009/04/09/nhibernate-mapping-ltmany-to-onegt.aspx

http://nhforge.org/doc/nh/en/index.html#collections-lazy

dotjoe
So what's the difference between the lazy and fetch attributes?
scottm
yes, you could also simply set lazy="false" on the many-to-one and it will issue the separate select statement right away. When you set fetch="join" I think lazy attribute has no meaning...I'm not 100% on that though.
dotjoe
I understand. Thanks!
scottm
A: 

Dont change the mapping for the test , your requirements should drive how objects are mapped . Your repository method is closing the session after fetching fromDB. You need to keep the session open for the whole test method. To figure out how to do that you need to tell us how you are managing your session . Are those tied to a transaction / threadlocal ?

Surya
That's not how I understand it. The test should be created first to drive your code creation (i.e. the first run of your test should fail). I understand your point, but I'm still new to NHibernate so I can't just map it the way I've designed right off.
scottm
ok sure . So you test should reflect how sessions are being managed in real case. Is it session per operation like in you test?. If that is the case sure go ahead make it non lazy ( i personally very rarely make anything not lazy )
Surya
I think my problem was the tutorial I followed. Now that I have a little better understanding of NHibernate, I'll have to modify my repositories.
scottm