I have the entity defined below:
public class Foo : Entity<Foo.FooId>
{
public class FooId
{
public virtual String Bar { get; protected internal set; }
public virtual Int32 Buzz { get; protected internal set; }
}
// ...
}
And here's the base class:
public abstract class Entity<T> : IEquatable<Entity<T>>
{
public virtual T Id { get; protected internal set; }
// ...
}
I'm going to map the "Id" property as a "composite key", so I've added the following mapping class:
public class FooMap : ClassMapping<Foo>
{
public FooMap()
{
ComponentAsId(x => x.Id, m =>
{
m.Property(p => p.Bar);
m.Property(p => p.Buzz);
});
}
}
And that's all pretty nice, but I get an error with the following querying attempt:
session.QueryOver<Foo>()
.Where(m => m.Id.Bar == "a" &&
m.Id.Buzz == 2).List();
The error I get is:
NHibernate.QueryException : could not resolve property: Id of: Foo
It's quite strange, because by removing the base class and encapsulating everything within "Foo", it works like a charm.
Thanks in advance.
This was a bug and reported as NH-3105. It is now fixed in the most recent of the source code and will be released as 3.3.3.GA.
Related
What's wrong with my mapping shown below? Is this a problem with GeneratedBy.Foreign()? How should I use it cause my PK in UserTable(UID) is also the FK which refers to PersonTable PK(PID). I get the Duplicate class/entity mapping consoleMappingTest.SystemUser error. what do you suggest(be sure to look at database structure- no way to change it). thanks.
Inheritance structure:
public class Person
{
public virtual int ID { get; set; }
}
public class User:Person
{
public override int ID
{
get
{
return base.ID;
}
set
{
base.ID = value;
}
}
public virtual string Name { get; set; }
public virtual int Salary { get; set; }
}
public class SystemUser:User
{
public virtual int Password { get; set; }
}
Database structure:
for saving some info about person(some fields not shown here):
PersonTable(PID)
for saving User and all it's subclasses like system user:
UserTable(UID,Name,Salary,Type)
and here is my mapping:
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("PersonTable");
Id(x => x.ID, "PID").GeneratedBy.Assigned();//or HiLo-not important
}
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("UserTable");
DiscriminateSubClassesOnColumn("Type").Default("U");
Id(x => x.ID, "UID").GeneratedBy.Foreign("Person");//how should use this?
Map(x => x.Salary);
Join("PTable", j =>
{
j.KeyColumn("UID");
j.Map(x => x.Name);
});
}
}
public class SystemUserMap : SubclassMap<SystemUser>
{
public SystemUserMap()
{
DiscriminatorValue("SU");
Map(x => x.Password);
}
}
Foreign("") is meant to point to a Reference (Property with another mapped entity) from which the Id should be retrieved. You don't have a Reference to class Person named Person so you can't use it like this.
you already asked the same question with an answer. I know i didn't do it right first shot but would be nice if you told me what doesnt work with the latest edit or you dont like the solution befor asking the same question again
I have 2 classes:
public class MyBaseClass
{
public virtual int Id { get; set; }
public virtual string BaseProperty { get; set; }
}
public class MyClass : MyBaseClass
{
public virtual string ChildProperty { get; set; }
}
I want to map each of them to its own table (fluent NH). How to do it with no discriminator type column added to [MyBaseClass] table? So I expect [MyBaseClass] table consists of BaseProperty and Id columns only, MyClass consists of Id, BaseProperty and ChildProperty columns.
Thanks
You can try to put IgnoreBase to on MyBaseClass. It will say for FNH to map those classes independently
I've just found this (http://wiki.fluentnhibernate.org/Fluent_mapping#Components):
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Child : Parent
{
public string AnotherProperty { get; set; }
}
If you wanted to map this as a
table-per-subclass, you'd do it like
this:
public class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
Id(x => x.Id);
Map(x => x.Name);
}
}
public class ChildMap : SubclassMap<Child>
{
public ChildMap()
{
Map(x => x.AnotherProperty);
}
}
So looks like this approach does not require any Db changes like adding special fields to my tables. The only problem is I do not know how to do the same in AutoMapping with Override statements. We do mapping this way:
public class AutoMappingConfiguration : DefaultAutomappingConfiguration
{
public override bool IsDiscriminated(Type type)
{
return true;
}
public override bool ShouldMap(Type type)
{
return type.In(typeof(MyBaseClass),typeof(MyClass),...)
}
...
}
FluentNHibernate.Automapping.AutoPersistenceModel Instance =
AutoMap.AssemblyOf<MyBaseClass>(new AutoMappingConfiguration())
.Override<MyBaseClass>(m =>
{
...
}
}
So I'm not sure how to apply SubClass instruction in my case. Any advise?
Thanks.
This is very similar to my previous question: FluentNHibernate: How to translate HasMany(x => x.Addresses).KeyColumn("PersonId") into automapping
Say I have these models:
public class Person
{
public virtual int Id { get; private set; }
public virtual ICollection<Address> Addresses { get; private set; }
}
public class Address
{
public virtual int Id { get; private set; }
public virtual Person Owner { get; set; }
}
I want FluentNHibernate to create the following tables:
Person
PersonId
Address
AddressId
OwnerId
This can be easily achieved by using fluent mapping:
public class PersonMapping : ClassMap<Person>
{
public PersonMapping()
{
Id(x => x.Id).Column("PersonId");
HasMany(x => x.Addresses).KeyColumn("OwnerId");
}
}
public class AddressMapping : ClassMap<Address>
{
public AddressMapping()
{
Id(x => x.Id).Column("AddressId");
References(x => x.Person).Column("OwnerId");
}
}
I want to get the same result by using auto mapping. I tried the following conventions:
class PrimaryKeyNameConvention : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
instance.Column(instance.EntityType.Name + "Id");
}
}
class ReferenceNameConvention : IReferenceConvention
{
public void Apply(IManyToOneInstance instance)
{
instance.Column(string.Format("{0}Id", instance.Name));
}
}
// Copied from #Fourth: https://stackoverflow.com/questions/6091290/fluentnhibernate-how-to-translate-hasmanyx-x-addresses-keycolumnpersonid/6091307#6091307
public class SimpleForeignKeyConvention : ForeignKeyConvention
{
protected override string GetKeyName(Member property, Type type)
{
if(property == null)
return type.Name + "Id";
return property.Name + "Id";
}
}
But it created the following tables:
Person
PersonId
Address
AddressId
OwnerId
PersonId // this column should not exist
So I added a AutoMappingOverride:
public class PersonMappingOverride : IAutoMappingOverride<Person>
{
public void Override(AutoMapping<Person> mapping)
{
mapping.HasMany(x => x.Addresses).KeyColumn("OwnerId");
}
}
This correctly solved the problem. But I want to get the same result using attribute & convention. I tried:
public class Person
{
public virtual int Id { get; private set; }
[KeyColumn("OwnerId")]
public virtual ICollection<Address> Addresses { get; private set; }
}
class KeyColumnAttribute : Attribute
{
public readonly string Name;
public KeyColumnAttribute(string name)
{
Name = name;
}
}
class KeyColumnConvention: IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
var keyColumnAttribute = (KeyColumnAttribute)Attribute.GetCustomAttribute(instance.Member, typeof(KeyColumnAttribute));
if (keyColumnAttribute != null)
{
instance.Key.Column(keyColumnAttribute.Name);
}
}
}
But it created these tables:
Person
PersonId
Address
AddressId
OwnerId
PersonId // this column should not exist
Below is the rest of my code:
ISessionFactory sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString))
.Mappings(m =>
m.AutoMappings.Add(AutoMap.Assemblies(typeof(Person).Assembly)
.Conventions.Add(typeof(PrimaryKeyNameConvention))
.Conventions.Add(typeof(PrimaryKeyNameConvention))
.Conventions.Add(typeof(ReferenceNameConvention))
.Conventions.Add(typeof(SimpleForeignKeyConvention))
.Conventions.Add(typeof(KeyColumnConvention)))
//m.FluentMappings
// .Add(typeof (PersonMapping))
// .Add(typeof (AddressMapping))
)
.ExposeConfiguration(BuildSchema)
.BuildConfiguration()
.BuildSessionFactory();
Any ideas? Thanks.
Update:
The test project can be downloaded from here.
Sigh... Learning NHibernate is really a hair pulling experience.
Anyway I think I finally figured out how to solve this problem: Just remove the SimpleForeignKeyConvention and everything will work fine.
It seems the SimpleForeignKeyConvention conflicts with both ReferenceKeyConvention & KeyColumnConvention. It has higher priority than KeyColumnConvention but lower priority than ReferenceKeyConvention.
public class SimpleForeignKeyConvention : ForeignKeyConvention
{
protected override string GetKeyName(Member property, Type type)
{
if(property == null)
// This line will disable `KeyColumnConvention`
return type.Name + "Id";
// This line has no effect when `ReferenceKeyConvention` is enabled.
return property.Name + "Id";
}
}
I've tested your classes with FHN's auto-mapping feature and it does not create that second PersonId on Address table.
I'm using FHN v1.2.0.721 from here
I am using Fluent NHibernate to map the following classes:
public abstract class DomainObject
{
public virtual int Id { get; protected internal set; }
}
public class Attribute
{
public virtual string Name { get; set; }
}
public class AttributeRule
{
public virtual Attribute Attribute { get; set; }
public virtual Station Station { get; set; }
public virtual RuleTypeId RuleTypeId { get; set; }
}
public class Station : DomainObject
{
public virtual IList<AttributeRule> AttributeRules { get; set; }
public Station()
{
AttributeRules = new List<AttributeRule>();
}
}
My Fluent NHibernate mappings look like this:
public class AttributeMap : ClassMap<Attribute>
{
public AttributeMap()
{
Id(o => o.Id);
Map(o => o.Name);
}
}
public class AttributeRuleMap : ClassMap<AttributeRule>
{
public AttributeRuleMap()
{
Id(o => o.Id);
Map(o => o.RuleTypeId);
References(o => o.Attribute).Fetch.Join();
References(o => o.Station);
}
}
public class StationMap : ClassMap<Station>
{
public StationMap()
{
Id(o => o.Id);
HasMany(o => o.AttributeRules).Inverse();
}
}
I would like to order the AttributeRules list on Station by the Attribute.Name property, but doing the following does not work:
HasMany(o => o.AttributeRules).Inverse().OrderBy("Attribute.Name");
I have not found a way to do this yet in the mappings. I could create a IQuery or ICriteria to do this for me, but ideally I would just like to have the AttributeRules list sorted when I ask for it.
Any advice on how to do this mapping?
I think the OrderBy-method takes in the string that it inserts to the generated SQL-clause. So just doing
HasMany(o => o.AttributeRules).Inverse().OrderBy("Name");
Where the "Name" is the name of the column that contains Attribute's name. It should be in the column list because Attribute is joined to the AttributeRule.
Did you solve this other way? Please share.
I am pretty new to S#harp architecture and fluent nhibernate. I was trying to build a sample solution using the template.
MappingIntegrationTest fails for me when I try to run it for the following domains
public class Component
{
public virtual string comp { get; set; }
}
public class Parent : Entity
{
public virtual string Type { get; set; }
}
public class Child1: Parent
{
public virtual Component Blah { get; set }
}
The ParentMap looks like following:
public class ParentMap : IAutoMappingOverride<Parent>
{
public void Override(AutoMapping<Parent> mapping)
{
mapping.DiscriminateSubClassesOnColumn("Type")
.SubClass<Child1>(m =>
{
m.Component(c => c.Blah, c =>
{
c.Map(x => x.comp , "comp");
}
}
}
}
The mapping integration tests fail for me
* Database was not configured through Database method.
----> NHibernate.MappingException : An association from the table Parent refers to an unmapped class: Component
Do I need to remove these classes from AutoMapper?