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();
Related
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);
});
}
}
I am trying to map two tables :
the first one has an id (idA) and a field with the id (idB) of the other table.
the other one has a composite key based on the previous id (idB) and an other one (idB2)
The idea is that the second table contains the description of A split on multiple rows.
My current implementation is the following but I am unable to retrieve the partialDescription needed for concatenation.
How should I change my mapping to work? Any ideas? :)
public class A
{
long idA
lond idB
string fullDescription
}
public class B
{
long idB
long idB2
strind partialDescription
}
public class AMap : ClassMap<A>
{
public AMap()
{
Table("A");
Id(x => x.id).Column("idA").GeneratedBy.Native();
Map(x => x.idB).Column("idB")
HasMany(x => x.B).KeyColumn("idB").Inverse().Cascade.All().Not.LazyLoad();
}
}
public class BMap : ClassMap<B>
{
public BMap()
{
Table("B");
CompositeId()
.KeyReference(x => x.A, "idB")
.KeyProperty(x => x.idB2, "idB2");
Map(x => x.partialDescription, "desc").CustomType("AnsiString");
}
}
In fact, by your description, you must have this another scenario:
public class A
{
public virtual long Id { get; set; }
public virtual IList<B> PartialDescriptions { get; protected set; }
public string fullDescription
{
get
{
StringBuilder description = new StringBuilder();
foreach (var partial in PartialDescriptions)
{
description.Append(partial);
}
return description.ToString();
}
}
}
public class B
{
public virtual long Id { get; set; }
public virtual long Id2 { get; set; }
public virtual string Description { get; set; }
}
Then, try to implement your class maps like this:
public class BMap : ClassMap<B>
{
public BMap()
{
Table("B");
CompositeId()
.KeyReference(x => x.Id, "idB")
.KeyProperty(x => x.Id2, "idB2");
Map(x => x.partialDescription, "desc").CustomType("AnsiString");
}
}
public class AMap : ClassMap<A>
{
public AMap()
{
Table("A");
Id(x => x.Dd).Column("idA").GeneratedBy.Native();
HasMany(x => x.PartialDescriptions)
.KeyColumn("idB")
.Inverse()
.Cascade.All()
.Not.LazyLoad();
}
}
NOTE: I didn't tried to compile this code. I only expect that you can take the general .
I recommend you that take a look into Getting started section of Fluent NHibernate to more information.
If I have the following (cut down for brevity)
public class TempCartMap : ClassMap<TempCart>
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Products); // < This one
}
[Serializable]
public class TempCart {
public TempCart(){
Products = new List<int>();
}
public virtual IList<int> Products { get; set; }
}
I've looked at (http://www.philliphaydon.com/2012/06/using-nhibernate-with-servicestack/) and (http://www.philliphaydon.com/2012/03/ormlite-blobbing-done-with-nhibernate-and-serialized-json/) but is there a shorter, simpler, faster way of getting NHibernate to Serialize & De-serialize a column as above.
Seems overkill to create a new IUserType class etc etc.
You can also store the Products-list as a separate table:
public class TempCartMap : ClassMap<TempCart>
{
Id(x => x.Id).GeneratedBy.Identity();
HasMany(x => x.Products)
.Element("product_number")
.KeyColumn("temp_cart_id")
.Table("temp_cart_products")
;
}
Just to clarify the question: is there a reason why this is not wanted?
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");
});
}
}
I have a mapping structured in this way:
public class Person
{
public IDictionary<bool, Action> Actions { get; set; }
}
public class Action
{
public string Name { get; set; }
}
// Map for Person
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Id(x => x.Id) ...
Map(x => x.Name) ...
Table("Persons")
}
}
// Map for Action
public class ActionMap : ActionMap<Action>
{
public ActionMap()
{
Id(x => x.Id) ...
Map(x => x.Name) ...
Table("Actions")
}
}
What I need to do now is this.
I need a third table that will contains this fields:
PersonId
ActionId
True/false
Because I have the collection of actions inside the class person i was thinking about using a manytomany, but I can't find documentation on how to map an IDictionary.
Any idea? Wrong approach?
Dunno if you've already found the solution but there's been a recent update(only a couple weeks ago) where you could simply map it as HasManyToMany(x => x.NameOfDictionary) so you might want to update your FNH.
I haven't tried it yet though but here's the link: mailing list post