NHibernate 3.2 many to many mapping by code - nhibernate

I'm trying to learn NHibernate 3.2 built-in mapping by code api (NOT FluentNHibernate, nor xml). Can you help me to map a many-to-many relationship between these entities please?
public class Post {
public virtual Id { get; set; }
public IList<Tag> Tags { get; set; }
}
public class Tag {
public virtual Id { get; set; }
public IList<Post> Posts { get; set; }
}
My primary key strategy is:
Id(
t => t.Id,
t => {
t.Generator(Generators.HighLow, g => g.Params(new { max_low = 100 }));
t.Column(typeof(TEntity).Name + "Id");
});
and I try this:
// TagMap : ClassMapping<Tag>
Bag(t => t.Posts, bag => {
bag.Inverse(true);
bag.Table("TagsPosts");
bag.Cascade(Cascade.DeleteOrphans);
}, t => t.ManyToMany(c => {
c.Column("PostId");
c.Lazy(LazyRelation.Proxy);
}));
// PostMap : ClassMapping<Post>
Bag(t => t.Tags, bag => {
bag.Table("TagsPosts");
bag.Cascade(Cascade.DeleteOrphans);
}, t => t.ManyToMany(c => {
c.Column("TagId");
c.Lazy(LazyRelation.Proxy);
}));
but it doesn't work.

// Post Map
Bag(x => x.Tags, collectionMapping =>
{
collectionMapping.Table("TagPosts");
collectionMapping.Cascade(Cascade.None);
collectionMapping.Key(k => k.Column("PostID"));
},
map => map.ManyToMany(p => p.Column("TagID")));
// Tag Map
Bag(x => x.Posts, collectionMapping =>
{
collectionMapping.Table("TagPosts");
collectionMapping.Cascade(Cascade.None);
collectionMapping.Key(k => k.Column("TagID"));
},
map => map.ManyToMany(p => p.Column("PostID")));

I think you need to set the key to tell nhibernate which column on the many-to-many table it needs to check, something like this:
// TagMap
bag.Key(k => k.Column("TagId"));
// PostMap
bag.Key(k => k.Column("PostId"));

Related

Fluent Nhibernate with SQL joining on to a child collection with a disrimitor value

here are my parent class mappings:
public class Result : BaseEntity<long, Result>
{
public virtual string Stuff { get; set; }
public virtual Iesi.Collections.Generic.ISet<ResultRequestParam> Request { get; set; }
public virtual Iesi.Collections.Generic.ISet<ResultResponseParam> Response { get; set; }
public Result()
{
Request = new HashedSet<Request>();
Response = new HashedSet<Response>();
}
}
public class ResultMap : ClassMapping<Result>
{
public ResultMap()
{
Id(x => x.Id, map => map.Generator(Generators.Identity));
Property(x => x.Stuff, map => { map.NotNullable(false); map.Length(256); });
Set(x => x.Request, map =>
{
map.Key(x => x.Column("ResultId"));
map.Cascade(Cascade.Persist | Cascade.Remove | Cascade.DeleteOrphans);
map.Inverse(true);
},
m => m.OneToMany());
Set(x => x.Response, map =>
{
map.Key(x => x.Column("ResultId"));
map.Cascade(Cascade.Persist | Cascade.Remove | Cascade.DeleteOrphans);
map.Inverse(true);
},
m => m.OneToMany());
}
}
here is my child class mapping:
public enum ResultParamType
{
Request = 0,
Response = 1
}
public abstract class ResultParam : BaseEntity<long, ResultParam>
{
public virtual string ParamKey { get; set; }
public virtual string ParamValue { get; set; }
public virtual Result Result { get; set; }
}
public class ResultParamMap : ClassMapping<ResultParam>
{
public ResultParamMap()
{
Table("ResultParam");
Id(x => x.Id, map => map.Generator(Generators.Identity));
Discriminator(x =>
{
x.Column("ParmType");
x.Type(NHibernateUtil.String);
x.Length(20);
x.Force(true);
x.NotNullable(true);
});
Property(x => x.ParamKey, map =>
{
map.Length(1000);
map.NotNullable(true);
});
Property(x => x.ParamValue, map =>
{
map.Length(3000);
map.NotNullable(false);
});
ManyToOne(x => x.Result, map =>
{
map.Column("ResultId");
map.NotNullable(true);
});
}
}
public class ResultRequestParam : ResultParam { }
public class ResultRequestParamMap : SubclassMapping<ResultRequestParam>
{
public ResultRequestParamMap()
{
DiscriminatorValue(ResultParamType.Request.ToString());
}
}
public class ResultResponseParam : ResultResponseParam { }
public class ResultResponseParamMap : SubclassMapping<ResultResponseParam>
{
public ResultResponseParamMap()
{
DiscriminatorValue(ResultParamType.Response.ToString());
}
}
Now In my provider I want to start my query with Result and then add restrictions for the child set. Similar to this query:
SELECT
*
FROM
[Result] a
JOIN [ResultParam] b on b.ResultId = a.Id
WHERE
b.ParamKey = 'hello' or b.ParamValue = 'hello'
But there's really no clear way to do this from what I've found. It seems everyone just starts with the child and works up to the parent. This wont work for me as I have two virtually mapped child entities via a discriminator value...
Welp since no one was of any help on this site I banged my head against the wall 45 times and just did a subquery. Example:
var subquery = QueryOver.Of<ResultRequestParam >()
.JoinAlias(x => x.Result, () => aR)
.Where(() => <filtering for child table>)
.Select(Projections.Distinct(Projections.Property(() => aR.Id)));
q = q.WithSubquery.WhereProperty(() => aR.Id).In(subquery);
I feel like this could be better but it works. Maybe this will help someone else who is stuck in Nhibernate hell.

NHibernate 3.3.1 mapping by code many-to-many relationship with property-ref

I have an pretty big database and I have to map two entities using NHibernate 3.3.1 byCode mapping (please take in account that I'm not using FluentNHibernate, but built-in mappings)
Entities are in a many to many relationship through and unique key, not primary key.
Here are my examples
public class Achievable : Entity
{
public Achievable()
{
Uniqueidentifier = Guid.NewGuid();
}
public virtual string Name { get; set; }
public virtual Guid Uniqueidentifier { get; set; }
public virtual IList<Kitting> Kittings { get; set; }
}
public class Kitting : Entity
{
public virtual string Name { get; set; }
public virtual IList<Achievable> Achievables { get; set; }
}
So I have to link Achievable table to Kitting using third table AchievableKittings through Uniqueidentifier property
I've written the following mapping classes :
public class AchievableMap : ClassMapping<Achievable>
{
public AchievableMap()
{
Property(x => x.Name);
Property(x => x.Uniqueidentifier, m =>
{
m.Unique(true);
m.NotNullable(true);
});
Bag(x => x.Kittings, m =>
{
m.Table("AchievableKittings");
m.Cascade(Cascade.Persist);
m.Inverse(false);
m.Key(k =>
{
k.PropertyRef(x => x.Uniqueidentifier);
k.Column("aUniqueindentifier");
k.ForeignKey("FK_Achievable_uniqueidentifier");
});
}, r => r.ManyToMany(m =>
{
m.Column("kittingId");
m.ForeignKey("FK_Kitting_kittingId");
}));
}
}
public class KittingMap : ClassMapping<Kitting>
{
public KittingMap()
{
Property(x => x.Name);
Bag(x => x.Achievables, m =>
{
m.Table("AchievableKittings");
m.Inverse(true);
m.Key(k =>
{
k.Column("kittingId");
k.ForeignKey("FK_Kitting_kittingId");
});
}, r => r.ManyToMany(m =>
{
m.Column("aUniqueindentifier");
m.ForeignKey("FK_Achievable_uniqueidentifier");
}));
}
}
It seams that NHibernate ignore k.PropertyRef(x => x.Uniqueidentifier) statement. My Primary keys are bigint generated by identity. Now when Nhiebrnate tries to create the db its create the FK key for AchievableKittings to primaryKey of achievable, but not property-ref Uniqueidentifier
alter table AchievableKittings
add constraint FK_Achievable_uniqueidentifier
foreign key (aUniqueindentifier)
references Achievable
and of course its throws an exception:
Column 'Achievable.AchievableId' is not the same data type as referencing column 'AchievableKittings.aUniqueindentifier' in foreign key 'FK_Achievable_uniqueidentifier'.
Using FluentNhibernate is was able to map this in a right way. Unfortunately I can not use FluentNhibernate in current project.
So could some one help me with this issue.
I do it like this and always use sets
Set(x => x.Users, d =>
{
d.Schema("dbo");
d.Inverse(false);
d.Access(Accessor.Property);
d.Table("UsersRoles");
d.Key(c => c.Column("RoleId"));
}, r => r.ManyToMany(c => { c.Class(typeof(User)); c.Column(n => n.Name("UserId")); }));

NHibernate.Mapping.ByCode Many-to-Many relations

I've created 2 objects:
public class Set
{
public Set()
{
_sorts = new List<Sort>();
}
public virtual int Id { get; set; }
public virtual string Code { get; set; }
private ICollection<Sort> _sorts;
public virtual ICollection<Sort> Sorts
{
get { return _sorts; }
set { _sorts = value; }
}
}
public class Sort
{
public Sort()
{
_sets = new List<Set>();
}
public virtual int Id { get; set; }
public virtual string Name { get; set; }
private ICollection<Set> _sets;
public virtual ICollection<Set> Sets
{
get { return _sets; }
set { _sets = value; }
}
}
And 2 mappings:
public class SetMapping: ClassMapping<Set>
{
public SetMapping()
{
Table("Sets");
Id(x => x.Id, map => map.Generator(IdGeneratorSelector.CreateGenerator()));
Property(x => x.Code, map =>
{
map.Length(50);
map.NotNullable(false);
});
Bag(x => x.Sorts, map =>
{
map.Key(k =>
{
k.Column("SetId");
k.NotNullable(true);
});
map.Cascade(Cascade.All);
map.Table("SetsToSorts");
map.Inverse(true);
}, r => r.ManyToMany(m => m.Column("SortId")));
}
}
public class SortMapping: ClassMapping<Sort>
{
public SortMapping()
{
Table("Sorts");
Id(x => x.Id, map => map.Generator(IdGeneratorSelector.CreateGenerator()));
Property(x => x.Name, map =>
{
map.Length(50);
map.NotNullable(false);
});
}
}
usage:
Set can have many sorts
Sort can belong to many sets.
And I would like to use this as:
var set = new Set() {Code = "001"};
var sort = new Sort {Name = "My name"};
set.Sorts.Add(sort);
sort.Sets.Add(set);
Somehow the relations are not working yet because when I try to use the above code to add sorts to set for example and commit then I don't see any records saved to the SetsToSorts linked table.
Does anyone have a clue what I'm missing in my mapping? Or otherwise doing wrong?
Thank you,
Joost
Your mapping says that Set's Sort collection is inverse (map.Inverse(true)). That means the other side of the bidirectional association is responsible for persisting changes.
But your Sort class mapping doesn't have any collection mapping. Remove map.Inverse(true) on SetMapping or add noninverse collection mapping to SortMapping.

Hierarchical entity parent key in NHibernate ByCode

Using NHibernate 3.2 ByCode configuration, I am attempting to map the following hierarchical entity:
public class BusinessType
{
public virtual Guid BusinessTypeId { get; set; }
public virtual Guid? ParentBusinessTypeId { get; set; }
public virtual String BusinessTypeName { get; set; }
public virtual ICollection<BusinessType> Children { get; set; }
}
with this ClassMapping:
public class BusinessTypeMapper : ClassMapping<BusinessType>
{
public BusinessTypeMapper()
{
Id(x => x.BusinessTypeId, x => x.Type(new GuidType()));
Property(x => x.ParentBusinessTypeId, x => x.Type(new GuidType()));
Property(x => x.BusinessTypeName);
Set(x => x.Children,
cm =>
{
// This works, but there is an ugly string in here
cm.Key(y => y.Column("ParentBusinessTypeId"));
cm.Inverse(true);
cm.OrderBy(bt => bt.BusinessTypeName);
cm.Lazy(CollectionLazy.NoLazy);
},
m => m.OneToMany());
}
}
This works fine, but I'd rather be able to specify the key of the relation using a lambda so that refactoring works. This seems to be available, as follows:
public class BusinessTypeMapper : ClassMapping<BusinessType>
{
public BusinessTypeMapper()
{
Id(x => x.BusinessTypeId, x => x.Type(new GuidType()));
Property(x => x.ParentBusinessTypeId, x => x.Type(new GuidType()));
Property(x => x.BusinessTypeName);
Set(x => x.Children,
cm =>
{
// This compiles and runs, but generates some other column
cm.Key(y => y.PropertyRef(bt => bt.ParentBusinessTypeId));
cm.Inverse(true);
cm.OrderBy(bt => bt.BusinessTypeName);
cm.Lazy(CollectionLazy.NoLazy);
},
m => m.OneToMany());
}
}
The problem is that this causes NHibernate to generate a column called businesstype_key, ignoring the already-configured ParentBusinessTypeId. Is there any way to make NHibernate use a lambda to specify the relation, rather than a string?
I never need to navigate from children to parents, only from parents
to children, so I hadn't thought it necessary
then remove public virtual Guid? ParentBusinessTypeId { get; set; } completly. NH will then only create "businesstype_key" (convention) and no "ParentBusinessTypeId". if you want to change that then you have to specify your prefered columnname with cm.Key(y => y.Column("yourpreferredColumnName"));

Nhibernate Conformist Mapping "Unable to determine type..."

The class:
public class SOPProcess : ISOPProcess
{
public virtual Guid Id { get; set; }
public virtual SOP SOP { get; set; }
public virtual ProcessType Type { get; set; }
public virtual SOPProcessInput Input { get; set; }
public virtual SOPProcessOutput Output { get; set; }
public virtual SOPProcessMeasures Measures { get; set; }
public virtual decimal YieldFactor { get; set; }
public virtual SOPProcess PreviousProcess { get; set; }
public virtual SOPProcess NextProcess { get; set; }
}
The Mapping:
public class SOPProcessMap : ClassMapping<SOPProcess>
{
public SOPProcessMap()
{
Id(s => s.Id, i => i.Generator(Generators.GuidComb));
Property(s => s.YieldFactor);
ManyToOne(s => s.SOP, m =>
{
m.Column("SopId");
m.Cascade(Cascade.All);
});
ManyToOne(s => s.Type, m =>
{
m.Column("ProcessTypeId");
m.Cascade(Cascade.All);
});
ManyToOne(s => s.NextProcess, m =>
{
m.Column("NextProcessId");
m.Cascade(Cascade.All);
});
ManyToOne(s => s.PreviousProcess, m =>
{
m.Column("PreviousProcessId");
m.Cascade(Cascade.All);
});
}
}
The Error:
NHibernate.MappingException: Could not determine type for: MES.ProcessManager.SOP.SOPProcess, MES.ProcessManager, for columns: NHibernate.Mapping.Column(id)
I hope it's something simple, this is my first project using the Conformist mapping, so maybe I'm just overlooking something.
From our discussion on the nhusers mailing list.
I ran across the same problems.
You haven't defined the type of relationship. See the line action => action.OneToMany()); in the mapping below.
public class SportMap : ClassMapping<Sport>
{
public SportMap()
{
Id(x => x.Id, map =>
{
map.Column("Id");
map.Generator(Generators.GuidComb);
});
Property(x => x.Name, map =>
{
map.NotNullable(true);
map.Length(50);
});
Bag(x => x.Positions, map =>
{
map.Key(k => k.Column(col => col.Name("SportId")));
map.Cascade(Cascade.All | Cascade.DeleteOrphans);
},
action => action.OneToMany());
Property(x => x.CreateDate);
Property(x => x.CreateUser);
Property(x => x.LastUpdateDate);
Property(x => x.LastUpdateUser);
}
}
It turned out that the problem was in my Set mappings in other classes. If you don't specify the action for the mapping, it throws this (misleading) error.