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

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.

Related

nHibernate map a filtered bag to a single property

I need to map an etity to a mater tabele of a DB.
This Entity has OneToMany with another.
I need to map a Collection othe the master entity to all rows of the Child table.
But I also need to map a Property with a single row getted from the child table and filtered by a criteria that return always only one row.
Someting like a Component but in a filtered child table.
This is My Mapping:
public class Test
{
public virtual string Id { get; set; }
public virtual string Description { get; set; }
public virtual IList<TestItem> Items { get; set; }
public virtual TestItem Item { get; set; }
public Test()
{
Items = new List<TestItem>();
}
}
public class TestItem
{
public virtual string Id { get; set; }
public virtual Test Test { get; set; }
public virtual string ItemCode { get; set; }
public virtual string ItemData { get; set; }
}
public class TestMap : ClassMapping<Test>
{
public TestMap()
{
Id(x => x.Id, m => m.Column("IDTest"));
//IPOTETICAL
SomeComponent(x => x.Item, c => // How to map a filtered collection to a single property??
{
c.Key(k =>
{
k.NotNullable(true);
k.Column("IDTest");
});
**// This Is the filter**
c.Filter("itemsFilter", f => f.Condition("ItemCode = :itemsCodeValue"));
}, r => r.OneToMany(m =>
{
m.NotFound(NotFoundMode.Exception);
m.Class(typeof(TestItem));
}));
Bag(x => x.Items, c => // All Child Rows
{
c.Key(k =>
{
k.NotNullable(true);
k.Column("IDTest");
});
c.Cascade(Cascade.All | Cascade.DeleteOrphans);
c.Lazy(CollectionLazy.NoLazy);
c.Inverse(true);
}, r => r.OneToMany(m =>
{
m.NotFound(NotFoundMode.Exception);
m.Class(typeof(TestItem));
}));
}
}
public class TestItemMap : ClassMapping<TestItem>
{
public TestItemMap()
{
Id(x => x.Id, m => m.Column("IDTestItem"));
ManyToOne(x => x.Test, m =>
{
m.Column("IDTest");
m.NotNullable(false);
m.Lazy(LazyRelation.NoLazy);
});
Property(x => x.ItemCode);
Property(x => x.ItemData);
}
}
I found something about DynamicComponent but I do not know if it is for me...
http://notherdev.blogspot.it/2012/01/mapping-by-code-dynamic-component.html
Thank You!!

nHibernate one-to-many best practice persintence to avoid INSERT NULL

I've a One to Many mapping.
An entity with a collection persisted on two table 1:N
This is the class and the mapping:
public class Test
{
public virtual string Id { get; set; }
public virtual string Description { get; set; }
public virtual IList<TestItem> Items { get; set; }
public Test()
{
Items = new List<TestItem>();
}
}
public class TestItem
{
public virtual string Id { get; set; }
public virtual Test Test { get; set; }
public virtual string ItemCode { get; set; }
public virtual string ItemData { get; set; }
}
public class TestMap : ClassMapping<Test>
{
public TestMap()
{
Id(x => x.Id, m => m.Column("IDTest"));
Bag(x => x.Items, c =>
{
c.Key(k =>
{
k.NotNullable(true);
k.Column("IDTest");
});
c.Cascade(Cascade.DeleteOrphans);
c.Lazy(CollectionLazy.NoLazy);
c.Inverse(true);
}, r => r.OneToMany(m =>
{
m.NotFound(NotFoundMode.Exception);
m.Class(typeof(TestItem));
}));
}
}
public class TestItemMap : ClassMapping<TestItem>
{
public TestItemMap()
{
Id(x => x.Id, m => m.Column("IDTestItem"));
ManyToOne(x => x.Test, m =>
{
m.Column("IDTest");
m.NotNullable(false);
m.Lazy(LazyRelation.NoLazy);
});
Property(x => x.ItemCode);
Property(x => x.ItemData);
}
}
And this is the code.
If I remove the marked line I get the error.
var session = SessionFactory.OpenSession();
using (var tr = session.BeginTransaction())
{
var test = new Test();
test.Id = "T01";
test.Description = "Desc TEST 01";
session.SaveOrUpdate(test); // If Removed Get INSERT ERROR on TestItem
var item = new TestItem { Id = "NEW01", ItemCode = "A", Test = test, ItemData = "New T01A" };
test.Items.Add(item);
session.SaveOrUpdate(item);
session.SaveOrUpdate(test);
tr.Commit();
}
My question is:
Is this the best practice to persist a "one to many" relation ?
It is possible to use the code below and save all only saving the header Row (test) and automatically cascading all inserts on the child table??
var session = SessionFactory.OpenSession();
using (var tr = session.BeginTransaction())
{
var test = new Test();
test.Id = "T01";
test.Description = "Desc TEST 01";
//session.SaveOrUpdate(test); // If Removed Get INSERT ERROR on TestItem
var item = new TestItem { Id = "NEW01", ItemCode = "A", Test = test, ItemData = "New T01A" };
test.Items.Add(item);
//session.SaveOrUpdate(item);
session.SaveOrUpdate(test);
tr.Commit();
}
I suppose I need to change something on my mapping code but i do not undestand what!!!
Many days of work, googling, stackoverflowing but nothing change the result.
Thank you!!!
Q: Is this the best practice to persist a "one to many" relation ?
A: If you're talking about a "composition" relationship, the cascading approach can be a good choice.
Q: It is possible to use the code below and save all only saving the header Row (test) and automatically cascading all inserts on the child table??
A: Yes it is possible. But your code should look like this:
public class TestMap : ClassMapping<Test>
{
public TestMap()
{
Id(x => x.Id, m => m.Column("IDTest"));
Bag(x => x.Items, c =>
{
c.Key(k =>
{
k.NotNullable(true);
k.Column("IDTest");
});
c.Cascade(Cascade.All | Cascade.DeleteOrphans); //<- My suggestion
c.Lazy(CollectionLazy.NoLazy);
c.Inverse(true);
}, r => r.OneToMany(m =>
{
m.NotFound(NotFoundMode.Exception);
m.Class(typeof(TestItem));
}));
}
}

NHibernate 3.2 many to many mapping by code

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

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.

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.