I have a class referencing a (template)text which has for each tenant and each use case several possible texts
class Class1
{
[...]
public virtual Text TitleName { get; set; }
}
class Text
{
public virtual int TenantId { get; set; }
public virtual string Key { get; set; }
public virtual int Number { get; set; }
public virtual string Value { get; set; }
}
Unfortunatly the tablestructure looks like
Table Class1
...
textnumber int,
Table Text
tenant int,
key varchar (10),
number int,
pkey(tenant, key, number);
because Class1 always refers to tenant = 0 (all tenants) and key = "class1text"
Edit:
i need .Where() but References() doenst have it only HasMany()
What i have so far:
public void TextMap : ClassMap<Text>
{
public TextMap()
{
Table("restexts");
CompositeId()
.KeyProperty(t => t.TenantId, "tenant")
.KeyProperty(t => t.Key, "name")
.KeyProperty(t => t.Number, "number");
Map(t => t.Value, "content");
}
}
public void Class1Map : ClassMap<Class1>
{
public TextMap()
{
// mapping rest
References(c => c.TitleName)
.Columns("textnumber", ??, ??); // column 2 and 3 missing, because always the same
}
}
Any Ideas?
you can map your classes in this way with FluentNHibernate, it's a workaround to map only one field in OneToMany relationship.
public Class1()
{
Table("TableName");
Id(x => x.MyId).Column("TableId");
.
.
.
HasMany<TextModel>(x => x.textnumber).KeyColumn("TableField");
}
public Text()
{
Table("TableName");
Id(x => x.MyId).Column("TableId");
.
.
.
References<Class1Model>(x => x.number,"TableColumn");
}
Then you can add in your query the filters on the other fields (tenent = 0 and key = "class1text")
I hope it's helpful
running out of time i mapped it like Map(c => c.TitleNameId, "textnumber"); and have to remember the tenant and key name everytime i need the titlename (which is using magic values :(
Related
I want to map a class that has a property of type ICollection<> using NHibernate mapping by code. The code below works. But I don't like the extra Person property within CarSet to make the mapping work.
public class PersonSet
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<CarSet> Cars { get; set; }
}
public class CarSet
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual PersonSet Person { get; set; }
}
public class PersonSetMap : ClassMapping<PersonSet>
{
public PersonSetMap()
{
Id(x => x.Id, m=>m.Generator(Generators.Identity));
Property(x=>x.Name);
Set(x => x.Cars, c =>
{
c.Key(k =>
{
k.Column("PersonId");
});
c.Cascade(Cascade.Persist);
c.Lazy(CollectionLazy.NoLazy);
}, r =>
{
r.OneToMany();
}
);
}
}
public class CarSetMap : ClassMapping<CarSet>
{
public CarSetMap()
{
Id(x => x.Id, m => m.Generator(Generators.Identity));
Property(x => x.Name);
ManyToOne(x => x.Person, m =>
{
m.Column("PersonId");
m.Cascade(Cascade.None);
m.NotNullable(true);
});
}
}
public void Save(){
using (var session = Cfg.Session)
using (var tx = session.BeginTransaction())
{
PersonSet John = new PersonSet { Name = PersonName.John };
John.Cars = new List<CarSet> {
new CarSet { Name = CarnName.BMW,Person = John},
new CarSet { Name = CarnName.BM,Person = John }};
session.Save(entity);
tx.Commit();
}
}
The code above generates SQL script below:
create table PersonSet (
Id INT IDENTITY NOT NULL,
Name NVARCHAR(255) null,
primary key (Id)
)
create table CarSet (
Id INT IDENTITY NOT NULL,
Name NVARCHAR(255) null,
PersonId INT not null,
primary key (id)
)
alter table CarSet
add constraint FKF967D6489A220265
foreign key (PersonId)
references PersonSet
What I want is to generate SQL script with difference shown below, and keep the rest the same:
create table CarSet (
Name NVARCHAR(255) null,
PersonId INT not null,
)
Ideally I want the CarSet like this instead:
public class CarSet
{
public virtual int PersonId { get; set; }
public virtual string Name { get; set; }
}
Any idea?
map Cars as ComponentCollection
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Car> Cars { get; set; }
}
class Car
{
public virtual Person Owner { get; set; }
public virtual string Name { get; set; }
}
public class PersonMap : ClassMapping<Person>
{
public PersonMap()
{
Id(x => x.Id, m => m.Generator(Generators.Identity));
Property(x => x.Name);
Set(x => x.Cars, c =>
{
c.Key(k => k.Column("PersonId"));
c.Cascade(NHibernate.Mapping.ByCode.Cascade.Persist);
c.Lazy(CollectionLazy.NoLazy);
}, r =>
{
r.Component(c =>
{
c.Parent(x => x.Owner);
c.Property(x => x.Name);
});
});
}
}
Your ideal solution isn't possible. To use a CarSet table without it's own ID column it has to be a component, but component sets can't have nullable columns. If it's ok for you to mark Name as not-null you can adapt the solution Firo posted.
If that's not ok you can at least solve your first request to remove the Person property. Just delete the property and mark the key column in your set mapping as not-nullable. CarSet will still be an entity (and therefore have it's own ID) but you don't need the reference to PersonSet in code.
Btw, why are your classes postfixed by Set? Just naming them Person and Car would be much better since they only represent one person or car, not a collection of them.
as the title says, I would like to create a many-to-one relationship using Fluent NHibernate. There are GroupEntries, which belong to a Group. The Group itself can have another Group as its parent.
These are my entities:
public class GroupEnty : IGroupEnty
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
...
public virtual IGroup Group { get; set; }
}
public class Group : IGroup
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
...
public virtual IGroup Parent { get; set; }
}
And these are the mapping files:
public class GroupEntryMap : ClassMap<GroupEntry>
{
public GroupEntryMap()
{
Table(TableNames.GroupEntry);
Id(x => x.Id).GeneratedBy.Native();
Map(x => x.Name).Not.Nullable();
...
References<Group>(x => x.Group);
}
}
public class GroupMap : ClassMap<Group>
{
public GroupMap()
{
Table(TableNames.Group);
Id(x => x.Id).GeneratedBy.Native();
Map(x => x.Name).Not.Nullable();
...
References<Group>(x => x.Parent);
}
}
With this configuration, Fluent NHibernate creates these tables:
GroupEntry
bigint Id string Name ... bigint Group_id
Group
bigint Id string Name ... bigint Parent_id bigint GroupEntry_id
I don't know why it creates the column "GroupEntry_id" in the "Group" table. I am only mapping the other side of the relation. Is there an error in my configuration or is this a bug?
The fact that "GroupEntry_id" is created with a "not null" constraint gives me a lot of trouble, otherwise I would probably not care.
I'd really appreciate any help on this, it has been bugging me for a while and I cannot find any posts with a similar problem.
Edit: I do NOT want to create a bidirectional association!
If you want a many-to-one where a Group has many Group Entries I would expect your models to look something like this:
public class GroupEntry : IGroupEntry
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
...
public virtual IGroup Group { get; set; }
}
public class Group : IGroup
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
...
public virtual IList<GroupEntry> GroupEntries { get; set; }
public virtual IGroup Parent { get; set; }
}
Notice that the Group has a list of its GroupEntry objects. You said:
I don't know why it creates the column "GroupEntry_id" in the "Group" table. I am only mapping the other side of the relation.
You need to map both sides of the relationship, the many side and the one side. Your mappings should look something like:
public GroupEntryMap()
{
Table(TableNames.GroupEntry);
Id(x => x.Id).GeneratedBy.Native();
Map(x => x.Name).Not.Nullable();
...
References<Group>(x => x.Group); //A GroupEntry belongs to one Group
}
}
public class GroupMap : ClassMap<Group>
{
public GroupMap()
{
Table(TableNames.Group);
Id(x => x.Id).GeneratedBy.Native();
Map(x => x.Name).Not.Nullable();
...
References<Group>(x => x.Parent);
//A Group has-many GroupEntry objects
HasMany<GroupEntry>(x => x.GroupEntries);
}
}
Check out the fluent wiki for more examples.
The solution was that I accidentally assigned the same table name for two different entities... Shame on me :(
Thanks a lot for the input though!
I am trying to create my own foreign key convention that will name the FK in "FK_SourceTable_TargetTable" format.
However, when I run it I end up with two foreign keys instead of one.
My custom foreign key convention looks like this:
public class OurForeignKeyConvention : ForeignKeyConvention
{
protected override string GetKeyName(Member property, Type type)
{
if (property == null)
return string.Format("FK_{0}Id", type.Name); // many-to-many, one-to-many, join
if (property.Name == type.Name)
return string.Format("FK_{0}_{1}", property.DeclaringType.Name, type.Name);
return string.Format("FK_{0}_{1}_{2}", property.DeclaringType.Name, property.Name, type.Name);
}
}
My code to exercise it:
[TestMethod]
public void ShouldBeAbleToBuildSchemaWithOurConventions()
{
var configuration = new Configuration();
configuration.Configure();
Fluently
.Configure(configuration)
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<Widget>()
.Conventions.Add<OurForeignKeyConvention>()
)
.BuildSessionFactory();
new SchemaExport(configuration).Create(false, true);
}
My classes and mappings:
public class Widget
{
public virtual int Id { get; set; }
public virtual string Description { get; set; }
public virtual WidgetType Type { get; set; }
public virtual ISet<WidgetFeature> Features { get; set; }
}
public class WidgetFeature
{
public virtual int Id { get; set; }
public virtual Widget Widget { get; set; }
public virtual string FeatureDescription { get; set; }
}
public class WidgetMap : ClassMap<Widget>
{
public WidgetMap()
{
Id(w => w.Id);
Map(w => w.Description);
HasMany(w => w.Features).Cascade.AllDeleteOrphan().Inverse();
}
}
public class WidgetFeatureMap : ClassMap<WidgetFeature>
{
public WidgetFeatureMap()
{
Id(w => w.Id);
Map(w => w.FeatureDescription);
References(w => w.Widget);
}
}
The end result is two foreign keys, one called what I want - FK_WidgetFeature_Widget - and another one called FK_WidgetId.
If I change OurForeignKeyConvention to always return the same name regardless of whether the "property" parameter is null then I correctly get a single FK - but I then cannot get the "SourceTable" part of my FK name.
Can anyone explain what I am doing wrong here? Why is GetKeyName called twice? And why does one of the calls not provide a value for the "property" parameter?
Doh. ForeignKeyConvention provides the name for the FK column. What I should have been using is the IHasManyConvention, which can be used to name the FK constraint itself.
public class OurForeignKeyConstraintNamingConvention : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
instance.Key.ForeignKey(string.Format("FK_{0}_{1}", instance.Relationship.Class.Name, instance.EntityType.Name));
}
}
I'm wanting to have a 1 to many relationship in NHibernate where the Child table only has access to it's parentsId. Or the foreign key in the DB.
I've tried the following setup:
public class ParentTable
{
public ParentTable()
{
_childRecords = new List<ChildTable>();
}
public virtual int ParentId { get; set; }
private IList<ChildTable> _childRecords;
public virtual IEnumerable<ChildTable> ChildRecords
{
get { return _childRecords; }
}
public void AddChildTable(string value)
{
_childRecords.Add(new ChildTable{ StringField = value });
}
}
public class ChildTable
{
public virtual int ChildTableId { get; set; }
public virtual string StringField { get; set; }
public virtual int ParentId { get; set; }
}
Mappings:
public class ParentTableMap : ClassMap<ParentTable>
{
public ParentTableMap()
{
Not.LazyLoad();
Id(x => x.ParentId);
HasMany(x => x.ChildRecords)
.Not.LazyLoad()
.KeyColumn("ParentId").Cascade.All()
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore);
}
}
public class ChildTableMap : ClassMap<ChildTable>
{
public ChildTableMap()
{
Not.LazyLoad();
Id(x => x.ChildTableId);
Map(x => x.StringField);
Map(x => x.ParentId).Not.Nullable();
}
}
The following test fails as it's trying to insert 0 into the ParentId column?
[TestFixture]
public class Tests
{
[Test]
public void SaveOrUpdate_ParentWithChildren_WillCreateParentWithChildRecordsHavingMatchingParentId()
{
int id;
using (var sessionForInsert = SessionProvider.SessionFactory.OpenSession())
{
using (var trx = sessionForInsert.BeginTransaction())
{
//Assign
var parent = new ParentTable();
parent.AddChildTable("Testing");
parent.AddChildTable("Testing2");
sessionForInsert.SaveOrUpdate(parent); // Fails here with DB constraint error
id = parent.ParentId;
}
}
using (var sessionForSelect = SessionProvider.SessionFactory.OpenSession())
{
//Action
var result = sessionForSelect.Get<ParentTable>(id);
Assert.AreEqual(id, result.ParentId);
Assert.AreEqual(id, result.ChildRecords.First().ParentId);
Assert.AreEqual(id, result.ChildRecords.Last().ParentId);
}
}
}
This is what it's trying to do:
exec sp_executesql N'INSERT INTO ChildTable (StringField, ParentId) VALUES (#p0, #p1); select SCOPE_IDENTITY()',N'#p0 nvarchar,#p1 int',#p0='Testing;,#p1=0
I realise I could set-up a reference to the Parent Class in the Child Class. However I'd like to avoid this if at all possible, due to circular references and the problems that will cause when serializing and de-serializing these classes.
Has anyone successfully set-up and 1 to many relationship like the above?
Thanks
Dave
I think you either need to:
Make the ParentId on ChildTable nullable, or
Change your id generators to something NHibernate can generate.
The second option is nice. Switch to Guid.Comb for your id's. There's a restriction on what object relational mappers can do. Specifically, it is recommended to let NHibernate generate the id's instead of the database. I think this (long) blog post explains it in detail: http://fabiomaulo.blogspot.com/2009/02/nh210-generators-behavior-explained.html.
Good luck!
The problem is that you are attempting to insert a parent and its children in one operation. To do this, NHibernate wants to insert the child records with a null ParentId then update ParentId after the parent record is inserted. This foreign key constraint causes this to fail.
The best solution is to map the relationship from child to parent. You don't have to publicly expose the parent, you could just expose its ParentId as int? if desired.
If that's unacceptable, you should be able to accomplish this by changing the order of operations. First, I would require the ParentId in ChildTable's constructor. Then change the operation order in the test to get it to pass.
public class ChildTable
{
public ChildTable(int parentId) { ParentId = parentId; }
public virtual int ChildTableId { get; set; }
public virtual string StringField { get; set; }
public virtual int ParentId { get; private set; }
}
using (var trx = sessionForInsert.BeginTransaction())
{
//Assign
var parent = new ParentTable();
sessionForInsert.Save(parent);
sessionForInsert.Flush(); // may not be needed
parent.AddChildTable("Testing");
parent.AddChildTable("Testing2");
trx.Commit();
id = parent.ParentId;
}
EDIT:
public class ChildTable
{
private ParentTable _parent;
public ChildTable(Parent parent) { _parent = parent; }
public virtual int ChildTableId { get; set; }
public virtual string StringField { get; set; }
public virtual int? ParentId
{
get { return _parent == null : null ? _parent.ParentId; }
}
}
public class ChildTableMap : ClassMap<ChildTable>
{
public ChildTableMap()
{
Not.LazyLoad();
Id(x => x.ChildTableId);
Map(x => x.StringField);
// From memory, I probably have this syntax wrong...
References(Reveal.Property<ParentTable>("Parent"), "ParentTableId")
.Access.CamelCaseField(Prefix.Underscore);
}
}
We're using FluentNHibernate and we have run into a problem where our object model requires data from two tables like so:
public class MyModel
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int FooId { get; set; }
public virtual string FooName { get; set; }
}
Where there is a MyModel table that has Id, Name, and FooId as a foreign key into the Foo table. The Foo tables contains Id and FooName.
This problem is very similar to another post here: Nhibernate: join tables and get single column from other table but I am trying to figure out how to do it with FluentNHibernate.
I can make the Id, Name, and FooId very easily..but mapping FooName I am having trouble with. This is my class map:
public class MyModelClassMap : ClassMap<MyModel>
{
public MyModelClassMap()
{
this.Id(a => a.Id).Column("AccountId").GeneratedBy.Identity();
this.Map(a => a.Name);
this.Map(a => a.FooId);
// my attempt to map FooName but it doesn't work
this.Join("Foo", join => join.KeyColumn("FooId").Map(a => a.FooName));
}
}
with that mapping I get this error:
The element 'class' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'join' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'joined-subclass, loader, sql-insert, sql-update, sql-delete, filter, resultset, query, sql-query' in namespace 'urn:nhibernate-mapping-2.2'.
any ideas?
I think you misunderstood something here.
Instead of having Id, Name, FooId and FooName in same class you need to create two classes
public class MyModel
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Foo Foo { get; set; }
}
public class Foo
{
public virtual int Id { get; set; }
public virtual string FooName { get; set; }
}
And your mapping classes for these:
public class MyModelMapping : ClassMap<MyModel>
{
public MyModelMapping()
{
this.Id(x => x.Id);
this.Map(x => x.Name);
this.References(x => x.Foo);
}
}
public class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
this.Id(x => x.Id);
this.Map(x => x.FooName);
}
}
this should help.
But remember Convention other Configuration :)