NHibernate.Mapping.ByCode Many-to-Many relations - nhibernate

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.

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.

After updating to nHibernate 4, cannot use ManyToMany Mappings. Could not determine type

After updating to nHibernate (4.0.2.4000 via nuget), the many to many mappings that previously worked now cause mapping exception "Could not determine type for: nHibernateManyToMany.IRole, nHibernateManyToMany, for columns: NHibernate.Mapping.Column(id)"
Seems to be only for many to many, and when the List is a interface (i.e. List<Role> vs List<IRole>).
Example code that now fails:
class Program
{
static void Main(string[] args)
{
var configuration = new Configuration().SetProperty(Environment.ReleaseConnections, "on_close")
.SetProperty(Environment.Dialect, typeof(SQLiteDialect).AssemblyQualifiedName)
.SetProperty(Environment.ConnectionDriver, typeof(SQLite20Driver).AssemblyQualifiedName)
.SetProperty(Environment.CollectionTypeFactoryClass, typeof(DefaultCollectionTypeFactory).AssemblyQualifiedName)
.SetProperty(Environment.CommandTimeout, "0");
var mapper = new ModelMapper();
mapper.AddMappings(new[] { typeof(EmployeeMapping), typeof(RoleMapping) });
var hbmMapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
hbmMapping.autoimport = false;
configuration.AddMapping(hbmMapping);
// this line will fail
var factory = configuration.BuildSessionFactory();
}
}
public class Employee
{
public virtual int Id { get; set; }
public virtual List<IRole> Roles { get; set; }
}
public interface IRole
{
int Id { get; set; }
string Description { get; set; }
}
public class Role : IRole
{
public virtual int Id { get; set; }
public virtual string Description { get; set; }
}
public class EmployeeMapping : ClassMapping<Employee>
{
public EmployeeMapping()
{
Id(c => c.Id, x =>
{
x.Type(NHibernateUtil.Int32);
x.Generator(Generators.Identity);
x.Column("EmployeeId");
});
Bag(x => x.Roles, m =>
{
m.Table("EmployeeRole");
m.Key(km =>
{
km.Column("EmployeeId");
km.NotNullable(true);
km.ForeignKey("FK_Role_Employee");
});
m.Lazy(CollectionLazy.Lazy);
}, er => er.ManyToMany(m =>
{
m.Class(typeof(Role));
m.Column("RoleId");
}));
}
}
public class RoleMapping : ClassMapping<Role>
{
public RoleMapping()
{
Id(c => c.Id, x =>
{
x.Type(NHibernateUtil.Int32);
x.Generator(Generators.Identity);
x.Column("RoleId");
});
Property(x => x.Description, c =>
{
c.Length(50);
c.NotNullable(true);
});
}
}
Any help or suggestions about where we could look for details on how this has changed since v3 would be appreciated.
Turned out to be a bug in nHibernate.
A pull request has been submitted here https://github.com/nhibernate/nhibernate-core/pull/385

breeze deep expand with inheritance

I have some troubles with breeze and expanding deep properties. Here's the code:
public abstract class BaseEntity
{
public virtual Guid? Id { get; set; }
}
public partial class Request : BaseEntity
{
public virtual IList<ServiceBase> Services { get; set; }
public Request()
{
this.Services = new List<ServiceBase>();
}
}
public abstract class ServiceBase : BaseEntity
{
public virtual Guid? RequestId { get; set; }
public virtual Request Request { get; set; }
public virtual IList<Contact> Contacts { get; set; }
public ServiceBase()
{
Contacts = new List<Contact>();
}
}
public class ServiceTranslation : ServiceBase
{
public virtual string MyProp { get; set; }
}
public class ServiceModification : ServiceBase
{
public virtual string MyProp2 { get; set; }
}
public class BaseMapping<T> : ClassMapping<T> where T : BaseEntity
{
public BaseMapping()
{
this.Lazy(true);
//Schema("DEMO");
Id(x => x.Id, map => { map.Generator(Generators.GuidComb); });
}
}
public RequestMap()
{
this.Bag<ServiceBase>(x => x.Services, colmap =>
{
//colmap.Schema("demo");
colmap.Key(x => x.Column("RequestId"));
colmap.Inverse(false);
colmap.Cascade(Cascade.All);
}, map =>
{
map.OneToMany();
});
}
}
public ServicebaseMap()
{
this.Property(x => x.RequestId, map =>
{
map.Column("RequestId");
map.Insert(false);
map.Update(false);
map.NotNullable(true);
});
this.ManyToOne(x => x.Request, map =>
{
map.Column("RequestId");
map.NotNullable(true);
map.Cascade(Cascade.None);
});
this.Bag<Contact>(x => x.Contacts, colmap =>
{
//colmap.Schema("demo");
colmap.Table("ServiceContact");
colmap.Key(x => { x.Column("ContactId"); });
colmap.Inverse(false);
colmap.Cascade(Cascade.All);
}, map =>
{
map.ManyToMany(x => { x.Column("ServiceId"); });
});
}
public class ServicetranslationMap : JoinedSubclassMapping<ServiceTranslation>
{
public ServicetranslationMap()
{
Property(x => x.MyProp);
}
}
public class ServiceModificationMap : JoinedSubclassMapping<ServiceModification>
{
public ServiceModificationMap()
{
Property(x => x.MyProp2);
}
}
A Request contains a collection of ServiceBase(Abstract) and those ServiceBase contains contacts.
If I try to expand like this:
http://localhost:61971/breeze/EAINHibernate/Requests?$expand=Services,Services.Contacts
Contacts are never expanded. Looking through the code, I found the ExpandMap used by breeze in the InitializeWithCascade methods of NHInitializer.cs contains the type of the base class. But, when expanding, the type of the object in the collection is the concrete type, either ServiceTranslation or either ServiceModification.
What can we do in that kind of situation? Can we expect a fix?
Good catch! Yes, that's a bug and you can expect a fix.
UPDATE
Fixed now in GitHub. Fix will be included in the next release.

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