How do I return only one specific item from hasmany relationship - nhibernate

Though I have benefited from the collective wisdom of this site many times, this is my first question here.
I have, say, three classes like so:
public class ClassA
{
public ClassA() {}
public virtual IList<ClassB> ClassBs { get; set; }
}
public class ClassB
{
public ClassB() {}
public virtual DateTime StartDate { get; set; }
public virtual DateTime? EndDate { get; set; }
public virtual ClassC SomeObject { get; set; }
}
public class ClassC
{
public ClassC() {}
public virtual string Name { get; set; }
}
I am using NHibernate (3.3.1.4) and FluentNHibernate (1.3.0.733), and the mapping files are:
public Class ClassAMap : ClassMap<ClassA>
{
HasMany(x => x.ClassBs);
}
public Class ClassBMap : ClassMap<ClassB>
{
Map(x => x.StartDate).Not.Nullable();
Map(x => x.EndDate).Nullable();
References(x => x.SomeData).Not.Nullable();
}
public Class ClassCMap : ClassMap<ClassC>
{
Map(x => x.Name).Not.Nullable();
}
There are also IDs and versioning but I somehow think they are irrelevant.
What I want to do is:
select all "SomeObject"s from ClassBs of all "ClassA"s which have their "EndDate"s null and which have the most current StartDate in their group.
I tried some juggling with QueryOver but the most I could get was an IList<IList<ClassB>> which is really far from what I want to accomplish.
Edit:
(I think) Following code accomplishes the task with Linq. But this code requires all records from the DB. This may not be a problem for ClassBs with up to 3 records or so per ClassA but for ClassBs with hundreds of records this means getting all those records from DB to use just one record from ClassBs of each ClassA in the DB.
IList<ClassC> classCLs = new List<ClassC>();
ClassB latest = null;
foreach (
IList<ClassB> classBLs in
Session.QueryOver<ClassA>()
.Select(c => c.ClassBs).List<IList<ClassB>>()
) {
latest = classBLs.Where(cB => cB.EndDate == null).Aggregate((curr, next) => next.StartDate > curr.StartDate ? next : curr);
if (latest != null && !classCLs.Contains(latest.SomeObject)) {
classCLs.Add(latest.SomeObject);
}
}

Assuming your ClassA has an Id property, maybe this, involving a subquery, can be of some help :
ClassA aAlias = null;
ClassB bAliasMax = null, bAlias = null;
var subQuery = QueryOver.Of<ClassA>().JoinAlias(a => a.ClassBs, () => bAliasMax)
.Where(Restrictions.On(() => bAliasMax.EndDate).IsNotNull)
.Where(a=>a.Id==aAlias.Id)
.Select(Projections.Max(() => bAliasMax.StartDate));
var result =
_laSession.QueryOver(() => aAlias)
.JoinAlias(a => a.ClassBs, () => bAlias)
.WithSubquery.WhereProperty(() => bAlias.StartDate).Eq(subQuery)
.Select(Projections.Property(() => bAlias.SomeObject)) // suggested adding from question's author
.List<ClassC>(); // see above
I guess there are more effective answers, which would involve grouping.

Related

Table-per-subclass fluent nhibernate not working

I have the following classes defined:
And these tables in my database:
My fluent NHibernate mappings are:
public class BusinessUnitMap : ClassMap<BusinessUnit>
{
public BusinessUnitMap()
{
Table("BusinessUnits");
Id(x => x.Id);
Map(x => x.Code);
Map(x => x.Name);
Map(x => x.ParentId);
Map(x => x.Type).Column("Type").CustomType<BusinessUnitType>();
}
}
public class CompanyMap : SubclassMap<Company>
{
public CompanyMap()
{
Table("CompanyData");
KeyColumn("BusinessUnitID");
Map(x => x.Something);
}
}
public class FranchiseeMap : SubclassMap<Franchisee>
{
public FranchiseeMap()
{
Table("FranchiseeData");
KeyColumn("BusinessUnitID");
Map(x => x.SomethingDifferent);
}
}
public class StoreMap : SubclassMap<Store>
{
public StoreMap()
{
Table("StoreData");
KeyColumn("BusinessUnitID");
Map(x => x.SomethingElse);
}
}
Question #1
As far as I can tell, my code and database are setup the same as every example I've been able to find. According to those articles, NHibernate is supposed to be smart enough to determine what subclass to instantiate when I query for a particular subclass. But, when I execute the following statement:
var result = Session.QueryOver<BusinessUnit>()
.Where(x => x.Code == "Acme")
.SingleOrDefault();
an exception is thrown because it can't create an instance of the abstract BusinessUnit class. The only way I can get this to work is to specify Company as the type argument for QueryOver.
I've confirmed that using a discriminator breaks since NHibernate is looking for all of the columns to exist in a single table. Without it, though, I struggle to see how NHibernate would know what type to instantiate.
What am I doing wrong? Is the problem in my mappings, the way I'm querying, ...?
Question #2
When I change the query to something like this:
public T WithCode<T>(String code)
where T : BusinessUnit
{
var result = Session.QueryOver<T>()
.Where(x => x.Code == code)
.SingleOrDefault();
return result;
}
I get an exception indicating that the UPDATE statement conflicts with a foreign key constraint. Update statement!!!! Clearly something is still not right. How can a QueryOver call result in an UPDATE statement? What am I missing?
it looks like your data is not consistent. It might be better to use discrimnator mapping with optional. If you dont really need a BusinessUnitType property in code then just delete everything around the property Type
public enum BusinessUnitType
{
Company,
Franchisee
}
public abstract class BusinessUnit
{
public virtual int Id { get; set; }
public virtual string Code { get; set; }
public virtual string Name { get; set; }
public virtual BusinessUnit Parent { get; set; }
public abstract BusinessUnitType Type { get; }
}
public class Company : BusinessUnit
{
public virtual string Something { get; set; }
public override BusinessUnitType Type { get { return BusinessUnitType.Company; } }
}
public class Franchisee : BusinessUnit
{
public virtual string SomethingDifferent { get; set; }
public override BusinessUnitType Type { get { return BusinessUnitType.Franchisee; } }
}
public class BusinessUnitMap : ClassMap<BusinessUnit>
{
public BusinessUnitMap()
{
Table("BusinessUnits");
Id(x => x.Id);
Map(x => x.Code);
Map(x => x.Name);
References(x => x.Parent);
DiscriminateSubClassesOnColumn("Type");
Map(x => x.Type, "Type")
.Access.None()
.CustomType<BusinessUnitType>().ReadOnly();
}
}
public class CompanyMap : SubclassMap<StrangeTablePerSubclass.Company>
{
public CompanyMap()
{
DiscriminatorValue((int)new Company().Type);
Join("CompanyData", join =>
{
join.KeyColumn("BusinessUnitID");
join.Optional();
join.Map(x => x.Something);
});
}
}
public class FranchiseeMap : SubclassMap<Franchisee>
{
public FranchiseeMap()
{
DiscriminatorValue((int)new Franchisee().Type);
Join("FranchiseeData", join =>
{
join.KeyColumn("BusinessUnitID");
join.Optional();
join.Map(x => x.SomethingDifferent);
});
}
}

FluentNHibernate: Nested component mapping results in NHiberate QueryException

Hi i have a problem with mapping in Nhibernate. When I run a linq query referring to one of the component classes of my entity, I get a QueryException as below:
could not resolve property: ClosedCases of: Project.Entities.Headline
I have one table with records which i want to map into multiple objects of my headline entity.
Question:
Is there something wrongly set up in my mapping?
My Headline entity is separated into multiple logical classes as you can see below
public class Headline:Entity
{
public virtual DateTime Date { get; set; }
public virtual TeamTarget Teamtarget { get; set; }
}
public class TeamTarget : Entity
{
public virtual DateTime FromDate { get; set; }
public virtual DateTime ToDate { get; set; }
public virtual TargetItem AchievedTarget { get; set; }
public virtual Team Team { get; set; }
}
public class TargetItem : Entity
{
public virtual decimal ClosedCases { get; set; }
public virtual decimal Invoicing { get; set; }
}
Mapping file:
public class HeadlineMap: ClassMap<Headline>
{
public HeadlineMap()
{
Table("Headlines.Headlines");
Id(x => x.Id).Column("HeadlinesId");
Map(x => x.Date);
Component(x => x.Teamtarget, t =>
{
t.References(x => x.Team).Column("TeamId").Cascade.None();
t.Component(x => x.AchievedTarget, ti =>
{
ti.Map(x => x.Invoicing).Column("TeamInvoicing");
ti.Map(x => x.ClosedCases).Column("TeamClosedCases");
});
});
The Linq query I am running looks like this:
decimal closedCases = _headlineRepository.All
.Where(x =>
x.Date >= DateTimeExtensionMethods.FirstDayOfMonthFromDateTime(selectMonthFromDate)
&& x.Date <= DateTimeExtensionMethods.LastDayOfMonthFromDateTime(selectMonthFromDate)
&& x.Teamtarget.Team.Id == teamId
).Average(x => x.Teamtarget.AchievedTarget.ClosedCases);
I know that i can access headline item by using team entity by team id and this works, just have problem with the mapping of achieved target.
any ideas?
I think your mapping is fine. But as far as I know, it isn't possible to query a nested component with Linq to NHibernate. It can't understand what joins to do, it becomes too complex.
I think it is possible using NHibernate 3's QueryOver API using JoinQueryOver or JoinAlias. You should read this: QueryOver in NH 3.0
Maybe something like this would work:
Headline headlineAlias = null;
TeamTarget targetAlias = null;
Team teamAlias = null;
TargetItem targetItemAlias = null;
var query = session.QueryOver<Headline>(() => headlineAlias)
.JoinAlias(() => headlineAlias.Teamtarget, () => targetAlias)
.JoinAlias(() => targetAlias.Team, () => teamAlias)
.JoinAlias(() => targetAlias.AchievedTarget, () => targetItemAlias)
.Where(x => x.Date >= DateTimeExtensionMethods.FirstDayOfMonthFromDateTime(selectMonthFromDate) && x.Date <= DateTimeExtensionMethods.LastDayOfMonthFromDateTime(selectMonthFromDate))
.And(() => teamAlias.Id == teamId)
.Select(Projections.Avg(() => targetItemAlias.ClosedCases))
.SingleOrDefault<decimal>();

Fluent NHibernate one to many not saving children

I am using Fluent NHibernate. This is a classic case of a one to many relationship. I have one Supply parent with many SupplyAmount children.
The Supply parent object is saving with correct info, but the amounts are not getting inserted into the db when I save the parent. What am I doing for the cascade not to work?
The entities are as follows:
public class Supply : BaseEntity
{
public Guid SupplyId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Comments { get; set; }
public virtual IList<SupplyAmount> Amounts { get; set; }
public Supply()
{
Amounts = new List<SupplyAmount>();
}
public virtual void AddAmount(SupplyAmount amount)
{
amount.Supply = this;
Amounts.Add(amount);
}
}
public class SupplyAmount : BaseEntity
{
public virtual Guid SupplymountId { get; set; }
public virtual Supply Supply { get; set; }
public virtual int Amount { get; set; }
}
And the mapping as follows:
public class SupplyMap : ClassMap<Supply>
{
public SupplyMap()
{
Id(x => x.SupplyId);
Map(x => x.LastName);
Map(x => x.FirstName);
Map(x => x.Comments);
HasMany<SupplyAmount>(x => x.Amounts)
.Inverse().Cascade.SaveUpdate()
.KeyColumn("SupplyAmountId")
.AsBag();
}
}
public class SupplyAmountMap : ClassMap<SupplyAmount>
{
public SupplyAmountMap()
{
Id(x => x.SupplyAmountId);
References(x => x.Supply, "SupplyId").Cascade.SaveUpdate();
Map(x => x.Amount);
}
}
And this is how I call it:
public SaveIt()
{
Supply sOrder = Supply();
sOrder.FirstName = "TestFirst";
sOrder.LastName = "TestLast";
sOrder.Comments = "TestComments";
for (int i = 0; i < 5; i++)
{
SupplyAmount amount = new SupplyAmount();
amount.Amount = 50;
amount.Supply = sOrder;
sOrder.AddAmount(amount);
}
// This call saves the Supply to the Supply table but none of the Amounts
// to the SupplyAmount table.
AddSupplyOrder(sOrder);
}
I know this is an old post but why not...
// This call saves the Supply to the Supply table but none of the Amounts
This comment in SaveIt() indicates you call the save on the Supply and not the amounts.
In this case you have your logic the wrong way around.
So to fix this:
SupplyMap -> The Inverse shouldn't be there for Amounts.
HasMany<SupplyAmount>(x => x.Amounts).Cascade.SaveUpdate();
SupplyAmountMap ->
remove References(x => x.Supply, "SupplyId").Cascade.SaveUpdate();
Replace it with
References<Supply>(x=>x.Supply);
You should now be right to call the save on your supply object only and it will cascade down to the amounts.
Session.Save(supply);
In your test after you have arrange the supply and supplyamount make sure you call a
Session.Flush()
after your save to force it in.
This isn't as important in code as you will usually run in transactions before recalling the supply object.
Cheers,
Choco
Also as a side note it usually not a good idea to be to verbose with fluentmappings. let the default stuff do it thing which is why I would recommend against the column naming hints.

References/has-a mapping thru 3 tables

My model object Reading has a Location but it's not a direct relationship in the database. In the DB, this "has-a" relationship or "reference" spans 3 tables, as shown in this snip:
My Reading maps to the ComponentReading table and i want my Location to map to the Location table. My ClassMap<Reading> class looks like this for now:
public class ReadingMap : ClassMap<Reading>
{
public ReadingMap()
{
Table("ComponentReading");
Id(x => x.ID).Column("ComponentReadingId");
//References(x => x.Location).Formula(
Join("VehicleReading", vr =>
{
Join("TrainReading", tr =>
{
tr.References(x => x.Location, "LocationId");
});
});
Map(x => x.TemperatureValue).Column("Temperature");
}
}
And here is my simple Location mapping:
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Id(x => x.ID).Column("LocationId");
Map(x => x.Name);
}
}
The commented References( method sort of shows what i want to achieve with the relationship between Reading and Location but obviously i can't express it to FNH as simply as the commented line suggests.
I don't think the Join( code is even nearly correct either, but it also tries to communicate the relationship that i'm after.
I hope someone can see what i'm trying to do here. Can you help me?
This question is related.
I think you cant nest joins that way. An ugly but pragmatic solution would be (untested):
class Reading
{
public virtual int ID { get; set; }
protected virtual Hidden.TrainReading m_trainReading;
public virtual Location Location
{ get { return m_trainReading.Location; } set { m_trainReading.Location = value; } }
public virtual int TemperatureValue { get; set; }
}
namespace Hidden
{
class TrainReading
{
public virtual int ID { get; set; }
public virtual int VehicleReadingId { get; set; }
public virtual Location Location { get; set; }
}
}
public class ReadingMap : ClassMap<Reading>
{
public ReadingMap()
{
Table("ComponentReading");
Id(x => x.ID).Column("ComponentReadingId");
References(Reveal.Member<Reading, Hidden.TrainReading>("m_trainReading"), "");
Map(x => x.TemperatureValue).Column("Temperature");
}
}
public class TrainReadingMap : ClassMap<Hidden.TrainReading>
{
public TrainReadingMap()
{
Table("TrainReading");
Id(x => x.ID).Column("TrainReadingId");
References(x => x.Location, "LocationId");
Join("VehicleReading", vr =>
{
vr.KeyColumn("TrainReadingId");
vr.Map(x => x.VehicleReadingId, "VehicleReadingId");
});
}
}

fluent nhibernate component one-to-many

I have couple of classes and want to map them correctly to database:
public class A
{
public virtual Guid Id { get; private set; }
public virtual ComponentClass Component { get; set; }
}
public class ComponentClass
{
public virtual IList<B> Elements { get;set; }
}
public class B
{
public virtual Guid Id { get; private set; }
public virtual DateTime Time { get; set; }
}
I map them using fluent mappings like that:
public class AMap : ClassMap<A>
{
public A() {
Id(x => x.Id);
Component(x => x.Component,
c => c.HasMany(x => x.Elements).Inverse().Cascade.All());
}
}
public class BMap : ClassMap<B>
{
public B() {
Id(x => x.Id);
Map(x => x.Time);
}
}
When I save my entity, I have class A mapped to one table and class B to another as expected.
But I have nulls in Component_id column.
Can you tell me what am I missing here?
I believe Components are supposed to be in the same table , as clearly stated in Ayende's blog post, as they serve only to make the data better represented as an object model. Be sure to read through his blog, it's probably one of the best nHibernate resources out there.
Ok, I've resolved my problem - I can use Id of my "parent" class. So the component mapping will become:
public class AMap : ClassMap<A>
{
public A() {
Id(x => x.Id);
Component(x => x.Component,
c => c.HasMany(x => x.Elements).Cascade.All().Column("Id"));
}
}
So obvious as I look at it now ... but It took me an hour.
If you have a one-to-many association direct to a collection of components (ie. without the ComponentClass wrapper as per the question) then you can map it directly:
HasMany(x => x.Elements)
.AsSet()
.Table("ElementTable")
.KeyColumn("KeyColumn")
.Cascade.All()
.Component(x =>
{
x.Map(c => c.Id);
x.Map(c => c.Time);
})
.LazyLoad();