Using NHibernate QueryOver with a complex scenario - nhibernate

I´m trying to use QueryOver in that scenario :
public class Class1
{
public virtual string Name { get; set; }
public virtual string Descripton { get; set; }
public virtual Class2 { get; set; }
public virtual IList<Class3> ListClass3{ get; set; }
... //SEVERAL OTHERS LISTS, PROPERTIES
}
public class Class2
{
public virtual string Name { get; set; }
... //SEVERAL OTHERS LISTS, PROPERTIES
}
public class Class3
{
public virtual string Name { get; set; }
public virtual Class4 { get; set; }
... //SEVERAL OTHERS LISTS, PROPERTIES
}
public class Class4
{
public virtual string Prop1 { get; set; }
public virtual string Prop2{ get; set; }
... //SEVERAL OTHERS LISTS, PROPERTIES
}
And my DTO :
public class ClassDTO
{
public string NameClass1 { get; set; }
public string DescriptonClass1 { get; set; }
public string NameClass2 { get; set; }
public virtual IList<Class3> Class3List { get; set; }
}
My problem is how get the IList ... Without that, thats working fine so far:
Class2 class2 = null;
IList<Class3> listClass3 = null;
var queryOver = Session.QueryOver<clsClass1>();
var list = queryOver
.JoinAlias(x => x.Class2, () => class2)
.JoinAlias(x => x.ListClass3, () => listClass3, JoinType.LeftOuterJoin)
.SelectList(list => list
.Select(c2 => c2.Name).WithAlias(() => myDTO.NameClass1)
.Select(c2 => class2.Name).WithAlias(() => myDTO.NameClass2)
//NEED GET LIST<CLASS3>
)
.TransformUsing(Transformers.AliasToBean<ClassDTO>())
.List<ClassDTO>();
Thats working fine, but I need to 'fill' the IList now... And if possible, get just the Prop1 and Prop2 from Class4...
Thanks

something that is close to what you want taking one roundtrip
// get ClassDTOs
Class2 class2 = null;
ClassDTO myDTO = null;
var results = Session.QueryOver<Class1>()
.JoinAlias(x => x.Class2, () => class2)
.SelectList(list => list
.Select(c1 => c1.Id).WithAlias(() => myDTO.IdClass1)
.Select(c1 => c1.Name).WithAlias(() => myDTO.NameClass1)
.Select(c1 => c1.Description).WithAlias(() => myDTO.DescriptionClass1)
.Select(() => class2.Name).WithAlias(() => myDTO.NameClass2)
)
.TransformUsing(Transformers.AliasToBean<ClassDTO>())
.Future<ClassDTO>();
// get Class3DTOs
Class3 class3 = null;
Class3DTO myClass3DTO = null;
var subresults = Session.QueryOver<Class1>()
.JoinAlias(x => x.Class3List , () => class3)
.JoinAlias(() => classe3.Class4 , () => class4)
.SelectList(list => list
.Select(c => c.Id)
.Select(() => class3.Name)
.Select(() => class4.Prop1)
.Select(() => class4.Prop2))
.Future<object[]>()
.ToLookup(array => (int)array[0], array => new myClass3DTO
{
NameClass3 = (string)array[1],
Prop1Class4 = (string)array[2],
Prop2Class4 = (string)array[3],
});
// assigne the lists to the dto
foreach (var result in results)
{
result.ListClass3 = subresults[result.IdClass1].ToList();
}
return results;

Related

How to get List of Parent Entities with Child Count In Nhibernate QueryOver

I have two classes :
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Child> Childrens { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
}
Now through Nhibernate QueryOver I want to get list of all Parent with no of Count of children in single query.
Expected output is ?:
ParentId Name ChildrenCount
1 ABC 10
2 CDE 5
can anyone help me .
Using this DTO for projection:
public class ParentDto
{
public int Id { get; set; }
public string Name { get; set; }
public int ChildrenCount { get; set; }
}
Use this query:
Child childAlias = null;
ParentDto dto = null;
var dtoParents = Session.QueryOver<Parent>()
.JoinAlias(x => x.Childrens, () => childAlias)
.SelectList(list => list
.SelectGroup(x => x.Id).WithAlias(() => dto.Id)
.SelectGroup(x => x.Name).WithAlias(() => dto.Name)
.SelectCount(() => childAlias.Id).WithAlias(() => dto.ChildrenCount))
.TransformUsing(Transformers.AliasToBean<ParentDto>())
.List<ParentDto>();
You can read more about QueryOver projections using DTOs here.

nhibernate child collection limitation

I have these classes:
public class Document
{
public Document()
{
Descriptions = new List<Descriptions>();
}
public virtual int Id { get; set; }
public virtual IList<DocumentDescription> Descriptions { get; set; }
}
public class DocumentDescription
{
public virtual int DocumentId { get; set; }
public virtual int LanguageId { get; set; }
}
and mappings:
public DocumentMap()
{
Id(x => x.Id);
HasMany(x => x.Descriptions).KeyColumn("DocumentId");
}
public DocumentDescriptionMap()
{
CompositeId()
.KeyProperty(x => x.DocumentId)
.KeyProperty(x => x.LanguageId);
}
my query is:
var query = Session.QueryOver<Document>().Where(x => x.Id.IsIn(documentIds)).List();
I need a query over solution to restrict DocumentDescriptions by few languages, which I will get run-time. I don't want to get all DocumentDescriptions for one Document (only few). Is it possible to set filter/limitation for a child collection?
So, I find out how to add additional join clause to my query:
DocumentDescription dd = null;
ICriterion criterion = Restrictions.On<DocumentDescription>(x => x.LanguageId).IsIn(languageIds.ToArray());
var query = Session.QueryOver<Document>().Where(x => x.Id.IsIn(documentIds));
query.Left.JoinQueryOver(x => x.Descriptions, () => dd, criterion);
SQL:
SELECT * FROM tDocument
LEFT OUTER JOIN tDocumentDescription ON tDocumentDescription.DocumentId = tDocument.Id AND tDocumentDescription.LanguageId IN (#languageIds)
WHERE tDocument.Id IN (#documentIds)

NHibernate - Invalid index for this SqlParameterCollection with Count (Mapping By Code)

I am having a real problem with NHibernate Mapping By Code and a Composite Key in one of my classes.
The Domain class is as follows:-
public partial class UserRole {
public virtual int UserId { get; set; }
public virtual int RoleId { get; set; }
//public virtual UserRoleId Id { get; set; }
public virtual User User { get; set; }
public virtual Role Role { get; set; }
[NotNullNotEmpty]
public virtual DateTime VqsCreateDate { get; set; }
public virtual DateTime? VqsUpdateDate { get; set; }
[NotNullNotEmpty]
public virtual bool VqsMarkedForDelete { get; set; }
[Length(50)]
public virtual string VqsCreateUserName { get; set; }
[Length(50)]
public virtual string VqsLastUpdatedUserName { get; set; }
[NotNullNotEmpty]
[Length(128)]
public virtual string VqsCreateSessionId { get; set; }
[Length(128)]
public virtual string VqsLastUpdatedSessionId { get; set; }
[NotNullNotEmpty]
[Length(15)]
public virtual string VqsCreateIpAddress { get; set; }
[Length(15)]
public virtual string VqsLastUpdatedIpAddress { get; set; }
[Length(255)]
public virtual string VqsCreateSessionInfo { get; set; }
[Length(255)]
public virtual string VqsLastUpdatedSessionInfo { get; set; }
[NotNullNotEmpty]
public virtual bool VqsAllowDelete { get; set; }
public virtual bool? VqsAllowEdit { get; set; }
[Length(50)]
public virtual string VqsRffu1 { get; set; }
[Length(50)]
public virtual string VqsRffu2 { get; set; }
[Length(50)]
public virtual string VqsRffu3 { get; set; }
#region NHibernate Composite Key Requirements
public override bool Equals(object obj) {
if (obj == null) return false;
var t = obj as UserRole;
if (t == null) return false;
if (UserId == t.UserId
&& RoleId == t.RoleId)
return true;
return false;
}
public override int GetHashCode() {
int hash = GetType().GetHashCode();
hash = (hash * 397) ^ UserId.GetHashCode();
hash = (hash * 397) ^ RoleId.GetHashCode();
return hash;
}
#endregion
}
And the mapping class is as follows:-
public partial class UserRoleMap : ClassMapping<UserRole> {
public UserRoleMap() {
Schema("Membership");
Lazy(true);
ComposedId(compId =>
{
compId.Property(x => x.UserId, m => m.Column("UserId"));
compId.Property(x => x.RoleId, m => m.Column("RoleId"));
});
Property(x => x.VqsCreateDate, map => map.NotNullable(true));
Property(x => x.VqsUpdateDate);
Property(x => x.VqsMarkedForDelete, map => map.NotNullable(true));
Property(x => x.VqsCreateUserName, map => map.Length(50));
Property(x => x.VqsLastUpdatedUserName, map => map.Length(50));
Property(x => x.VqsCreateSessionId, map => { map.NotNullable(true); map.Length(128); });
Property(x => x.VqsLastUpdatedSessionId, map => map.Length(128));
Property(x => x.VqsCreateIpAddress, map => { map.NotNullable(true); map.Length(15); });
Property(x => x.VqsLastUpdatedIpAddress, map => map.Length(15));
Property(x => x.VqsCreateSessionInfo, map => map.Length(255));
Property(x => x.VqsLastUpdatedSessionInfo, map => map.Length(255));
Property(x => x.VqsAllowDelete, map => map.NotNullable(true));
Property(x => x.VqsAllowEdit);
Property(x => x.VqsRffu1, map => map.Length(50));
Property(x => x.VqsRffu2, map => map.Length(50));
Property(x => x.VqsRffu3, map => map.Length(50));
ManyToOne(x => x.User, map =>
{
map.Column("UserId");
////map.PropertyRef("Id");
map.Cascade(Cascade.None);
});
ManyToOne(x => x.Role, map =>
{
map.Column("RoleId");
////map.PropertyRef("Id");
map.Cascade(Cascade.None);
});
}
}
Whenever I try to insert into this table I get the following error:-
Invalid index for this SqlParameterCollection with Count 'N'
I have looked at all the occurrences of this error on StackExchange and across the majority of the internet for the last day and am still no further on.
There seem to be a lot of examples for Xml and Fluent mapping, but very little for Mapping By Code.
I think I have tried every combination of suggestions, but all to no avail.
I am fully aware that the problem is related to the fact that I have the ID fields in the table referenced twice and this is ultimately causing the error,
The problem is that I need both the ID and Entity Fields in my class as they are used extensively throughout the code.
I seem to be experiencing the issue as I have the combination of a composite and foreign keys in my many to many table.
Any help that anyone could provide to preserve my sanity would be very much appreciated.
Many thanks in advance.
Simon
You can set Insert(false) and Update(false) to prevent Nhibernate from generating an update or insert statement which includes the User and Role columns (which do not exist).
ManyToOne(x => x.User, map =>
{
map.Column("UserId");
////map.PropertyRef("Id");
map.Cascade(Cascade.None);
map.Insert(false);
map.Update(false);
});
ManyToOne(x => x.Role, map =>
{
map.Column("RoleId");
////map.PropertyRef("Id");
map.Cascade(Cascade.None);
map.Insert(false);
map.Update(false);
});
That's the same as Not.Update() Not.Insert() in Fluent mapping.
If you now create a valid UserRole object and set bot IDs to valid user/role ID NHibernate should be able to persist your object.

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!!

How to Use AutoMapper to Map to Models With Inheritance and Nested Models

I have an Address entity with 2 sub types. Here's my simplified code:
public class Address {
public string Street1 { get; set; }
public string Country { get; set; }
}
public class UsAddress : Address {
public string State { get; set; }
}
public class CandianAddress : Address {
public string Providence { get; set; }
}
Here's my simplified view models:
public class LocationModel {
public string Street1 { get; set; }
}
public class UsLocationModel : LocationModel {
public string State { get; set; }
}
public class CaLocationModel : LocationModel {
public string Providence { get; set; }
}
public class AddressModel {
public int? Country { get; set; }
public UsLocationModel UsLocation { get; set; }
public CaLocationModel CaLocation { get; set; }
}
Here's my simplified AutoMapper config:
Mapper.CreateMap<Address, AddressModel>()
.Include<UsAddress, AddressModel>()
.Include<CanadianAddress, AddressModel>();
Mapper.CreateMap<UsAddress, AddressModel>();
Mapper.CreateMap<CanadianAddress, AddressModel>();
Mapper.CreateMap<Address, LocationModel>()
.Include<UsAddress, USLocationModel>()
.Include<CanadianAddress, CALocationModel>();
Mapper.CreateMap<UsAddress, USLocationModel>();
Mapper.CreateMap<CanadianAddress, CALocationModel>();
I can't figure out how to resolve the UsLocation and CaLocation properties on AddressModel...
I figured it out. Here's a simplified version of my Automapper config:
Mapper.CreateMap<Address, AddressModel>()
.Include<UsAddress, AddressModel>()
.Include<CanadianAddress, AddressModel>()
.ForMember(x => x.USLocation, a => a.Ignore())
.ForMember(x => x.CALocation, a => a.Ignore())
.ForMember(x => x.Country, a => a.ResolveUsing<HaveIdValueResolver<Country, int>>().FromMember(x => x.Country);
Mapper.CreateMap<UsAddress, AddressModel>()
.ForMember(x => x.USLocation, a => a.MapFrom(Mapper.Map<UsAddress, USLocationModel>))
.ForMember(x => x.CALocation, a => a.Ignore())
.ForMember(x => x.Country, a => a.ResolveUsing<HaveIdValueResolver<Country, int>>().FromMember(x => x.Country));
Mapper.CreateMap<CanadianAddress, AddressModel>()
.ForMember(x => x.USLocation, a => a.Ignore())
.ForMember(x => x.CALocation, a => a.MapFrom(Mapper.Map<CanadianAddress, CALocationModel>))
.ForMember(x => x.Country, a => a.ResolveUsing<HaveIdValueResolver<Country, int>>().FromMember(x => x.Country));
Mapper.CreateMap<Address, LocationModel>()
.Include<UsAddress, USLocationModel>()
.Include<CanadianAddress, CALocationModel>();
Mapper.CreateMap<UsAddress, USLocationModel>();
Mapper.CreateMap<CanadianAddress, CALocationModel>();
Here's a simplified sample test:
var usAddress = new FakeUsAddress("111 L St", new FakeState(id: 17));
var addressModel = Mapper.Map<UsAddress, AddressModel>(usAddress);
Assert.IsNotNull(addressModel.USLocationModel);
Assert.IsNull(addressModel.CALocationModel);
Assert.AreEqual(usAddress.Street1, addressModel.USLocationModel.Street1);
Assert.AreEqual(usAddress.State.Id, addressModel.USLocationModel.State);
Assert.AreEqual(usAddress.Country.Id, addressModel.USLocationModel.Country);