i have 3 tables in my database:
As you can see the ProjectsTags table is a bridge table
here is my fluent nhibernate mapping
ProjectMap.cs:
Map(x => x.Name).Not.Nullable();
HasMany(x => x.ProjectsTags).AsBag().Inverse()
.Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);
ProjectsTagsMap.cs:
References(x => x.Project).Not.Nullable();
References(x => x.Tag).Not.Nullable();
TagMap.cs:
Map(x => x.Name).Not.Nullable();
As you can see, i historically didn't have the Tag table linked to anything else. I now need to generate a report to show Tag and how often that tag is used so i need to join from Tag to ProjectsTag. i tried adding this line into the tagsmap:
HasMany(x => x.ProjectsTags).AsBag().Inverse()
.Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);
but when i go to update the name on a tag object and commit, i get this error:
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance
can anyone see anything wrong with what i added that would be causing this nhibernate exception when i simply update the Tag table. Again my goal is to be able to do something like:
Tag.ProjectTags.Count();
Here is some additional code as requested:
my Tag Class:
public class Tag
{
public virtual IList<ProjectTag> ProjectTags { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
}
Somewhere in your code, you should have dereferenced the original collection on your Project domain. I suspect that your code goes like this:
var project = Session.Get<Project>();
project.ProjectsTags = new List<ProjectsTags> { someProjectsTagsInstance };
Session.Save(project);
If this is the case, you should do this instead:
var project = Session.Get<Project>();
project.ProjectsTags.Clear();
project.ProjectsTags.Add(someProjectsTagsInstance);
Session.Save(project);
While a collection is not modified, NH can still think that it is. Something like this could be caused by a ghost update. From NHibernate 3.0 Cookbook, Jason Dentler (page 184): "As part of automatic dirty checking, NHibernate compares the original state of an entity to its current state. An otherwise unchanged entity may be updated unnecessarily because a type conversion caused this comparison to fail".
Ghost update of collection can be caused by code that looks like this:
public class Tag
{
private IList<ProjectTag> projectsTags;
public virtual IEnumerable<ProjectTag> ProjectsTags
{
get
{
return new ReadOnlyCollection<ProjectTag>(projectsTags);
}
set
{
projectsTags = (IList<ProjectTag>)value;
}
}
}
ProjectsTags property returns the collection in readonly wrapper, so client code cannot add or remove elements to/from the collection.
The error will appear even when name of a tag is not changed:
private void GhostTagUpdate(int id)
{
using (var session = OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var tag = session.Get<Tag>(id);
transaction.Commit();
}
}
}
ProjectsTags collection should be mapped with CamelCaseField access strategy to avoid ghost updated:
HasMany(x => x.ProjectsTags)
.Access.CamelCaseField()
.AsBag().Inverse().Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);
Anyway...
Your association seems to be diabolically complex. If ProjectsTags table should contains only id of tag and id of project, then it would be simpler to use FNH many-to-many bidirectional mapping:
public class Tag2Map : ClassMap<Tag2>
{
public Tag2Map()
{
Id(x => x.Id);
Map(x => x.Name);
HasManyToMany(x => x.Projects)
.AsBag()
.Cascade.None()
.Table("ProjectsTags")
.ParentKeyColumn("TagId")
.ChildKeyColumn("ProjectId");
}
}
public class Project2Map : ClassMap<Project2>
{
public Project2Map()
{
Id(x => x.Id);
Map(x => x.Name);
HasManyToMany(x => x.Tags)
.AsBag()
.Cascade.None()
.Inverse()
.Table("ProjectsTags")
.ParentKeyColumn("ProjectId")
.ChildKeyColumn("TagId");
}
}
Now there is no need for ProjectTag entity in the model. The count of how many times is given tag used can be retrieved in two ways:
Direct way: tag.Projects.Count()
- but it retrieves all projects from database.
Query way:
var tag = session.Get<Tag2>(tagId);
var count = session.Query<Project2>().Where(x => x.Tags.Contains(tag)).Count();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With