views:

336

answers:

1

Hi! When I test my many to many classes an error occurs:

System.ApplicationException: Actual count does not equal expected count.

Entities:

    public interface IEntity
    {
        int Id { get; set; }
    }

    public abstract class Entity : IEntity
    {
        public virtual int Id { get; set; }

        public virtual bool IsPersistent
        {
            get { return isPersistentObject(); }
        }

        public override bool Equals(object obj)
        {
            if (isPersistentObject())
            {
                var persistentObject = obj as Entity;
                return (persistentObject != null) && (Id == persistentObject.Id);
            }

            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return isPersistentObject() ? Id.GetHashCode() : base.GetHashCode();
        }

        private bool isPersistentObject()
        {
            return (Id != 0);
        }
    }

    public class Team : Entity
    {
        public virtual string Name { get; set; }
        public virtual ISet<Employee> Employees { get; set; }

        public Team()
        {
            Employees = new HashedSet<Employee>();
        }
    }

    public class Employee : Entity
    {
        public virtual string LastName { get; set; }
        public virtual string FirstName { get; set; }
        public virtual ISet<Team> Teams { get; set; }
        public virtual string EMail { get; set; }

        public Employee()
        {
            Teams = new HashedSet<Team>();
        }
    }

Mappings:

public class TeamMap : ClassMap<Team>
{
    public TeamMap()
    {
        // identity mapping
        Id(p => p.Id).Column("TeamID");

        // column mapping
        Map(p => p.Name);

        // relationship mapping
        HasManyToMany(m => m.Employees)
            .Table("EmployeeTeam")
            .LazyLoad()
            .Cascade.SaveUpdate()
            .AsSet()
            .ParentKeyColumn("TeamID")
            .ChildKeyColumn("EmployeeID");
    }
}

public class EmployeeMap : ClassMap<Employee>
{
    public EmployeeMap()
    {
        // identifier mapping
        Id(p => p.Id).Column("EmployeeID");

        // column mapping
        Map(p => p.EMail);
        Map(p => p.LastName);
        Map(p => p.FirstName);

        // relationship mapping
        HasManyToMany(m => m.Teams).Table("EmployeeTeam")
            .Inverse()
            .Cascade.SaveUpdate()
            .AsSet()
            .LazyLoad()
            .ParentKeyColumn("EmployeeID")
            .ChildKeyColumn("TeamID");
    }
}

Test:

[TestMethod]
public  void  CanCorrectlyMapEmployee()
{
    var team = new List<Team> {new Team() {Name = "Team1"}};

    new PersistenceSpecification<Employee>(_session)
        .CheckProperty(p => p.EMail, "Mail")
        .CheckProperty(p => p.FirstName, "Firstname")
        .CheckProperty(p => p.Id, 1)
        .CheckProperty(p => p.LastName, "Lastname")
        .CheckList(p => p.Teams,team )
        .VerifyTheMappings();
}

Whether I add an Employee or a Team my EmployeeTeam table is always empty.

I have tested it against SQLLite with FNH and manually against SQL Server 2008.

Does anybody of you have an idea to fix this?

edit:

I was amazed to find out that when I create an Employee and add 2 Teams to the Employee and load the created Employee he has 2 Teams. So it works fine. But when I look in my relationsip table EmployeeTeam then everything is empty. Can someone explain me why?

And does anybody know how I can use Fluent NHibernate to test my many to many relationship?

Thanks in advance!

+1  A: 

Employee map has inverse attribute. As you probably know, this means that when you save employee, relationship table (EmployeeTeam) will not be updated. To add/remove new relationship information you have to add employee to the Team and save Team.

So, in your case - don't test many to many on the side of employee, test it on the side of team. (If you'd like NHibernate to add records when you add team to employee, you'll have to invert "Inverse" attributes - give it to team, not employee, but then - same story with Team entity).

Why were you able to load Employee with teams? Because of session-level cache. You've probably saved and loaded Employee in the same ISession - this means that NHibernate returned you exactly reference to the same object, without loading it from the db. Try saving & loading in two different sessions and you'll see no team in Employee.Teams set.

Side note: It is considered a good practice to create methods that will enforce consistency between many-to-many relationships, that is - when you add Team to Employee, Employee is added to the team, sth. like this:

class Employee 
{
  // ...
  public void AddTeam(Team team)
  {
     // check for null, etc. 

     Teams.Add(team);
     team.Employees.Add(this);
  }
}

and very simillar method in the Team class.

maciejkow
well I miss a quote function on stackoverflow ... "[...] Because of session-level cache. You've probably saved and loaded Employee in the same ISession - this means that NHibernate returned you exactly reference to the same object, without loading it from the db." This is not correct. I tested this and yes NHibernate caches my objects but I cleared the cache and load then my objects. Furthermore I used SQL Server Profiler and there I saw a selecting Query with LEFT OUTER JOIN. So the retrieving and saving works, strangefully. But I will try out your tips. Thanks for now.
Rookian
The problem why the relationship was empty, was because I did not flush the session. Now Employee and Team can be saved correctly.
Rookian