I think I'm misunderstanding something about how this works. This is my fluent mapping:
public class FunctionInfoMap : ClassMap<FunctionInfo>
{
public FunctionInfoMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Signature);
Map(x => x.IsNative);
Map(x => x.ClassId);
References(x => x.Class, "ClassId");
HasMany(x => x.CallsAsParent).Inverse();
HasMany(x => x.CallsAsChild).Inverse();
Table("Functions");
}
}
public class CallMap : ClassMap<Call>
{
public CallMap()
{
CompositeId()
.KeyProperty(x => x.ThreadId)
.KeyProperty(x => x.ParentId)
.KeyProperty(x => x.ChildId)
.Mapped();
Map(x => x.ThreadId).Index("Calls_ThreadIndex");
Map(x => x.ParentId).Index("Calls_ParentIndex");
Map(x => x.ChildId).Index("Calls_ChildIndex");
Map(x => x.HitCount);
References(x => x.Thread, "ThreadId");
References(x => x.Parent, "ParentId");
References(x => x.Child, "ChildId");
Table("Calls");
}
}
public class SampleMap : ClassMap<Sample>
{
public SampleMap()
{
CompositeId()
.KeyProperty(x => x.ThreadId)
.KeyProperty(x => x.FunctionId)
.Mapped();
Map(x => x.ThreadId);
Map(x => x.FunctionId);
Map(x => x.HitCount);
References(x => x.Thread, "ThreadId");
References(x => x.Function, "FunctionId");
Table("Samples");
}
}
Now when I create this schema into a fresh database, that FunctionId field from SampleMap winds up in the Calls table.
create table Calls (
ThreadId INTEGER not null,
ParentId INTEGER not null,
ChildId INTEGER not null,
HitCount INTEGER,
FunctionId INTEGER,
primary key (ThreadId, ParentId, ChildId)
)
create table Samples (
ThreadId INTEGER not null,
FunctionId INTEGER not null,
HitCount INTEGER,
FunctionId INTEGER,
primary key (ThreadId, FunctionId)
)
I don't understand why it's there, since it should only exist in the Samples table.
You should not map both the foreign key and the many-ton-one relationship. Instead of
Map(x => x.ThreadId).Index("Calls_ThreadIndex");
Map(x => x.ParentId).Index("Calls_ParentIndex");
Map(x => x.ChildId).Index("Calls_ChildIndex");
Map(x => x.HitCount);
References(x => x.Thread, "ThreadId");
References(x => x.Parent, "ParentId");
References(x => x.Child, "ChildId");
map the many-to-ones which use the foreign keys
Map(x => x.HitCount);
References(x => x.Thread, "ThreadId");
References(x => x.Parent, "ParentId");
References(x => x.Child, "ChildId");
I don't know if this will solve your problem. Also, I strongly advise against using composite keys. Replace them with surrogate key (identity) and unique indexes.
I finally figured out the problem, more or less. The pair of collections on FunctionInfoMap were confusing NHibernate, since they don't actually lead anywhere. Adding KeyColumn entries to link them up to the Parent and Child associations corrected the stray field.
Related
Consider following mapping code please :
public sealed class BankMapping : ClassMap<Bank>
{
public BankMapping()
{
CompositeId()
.KeyProperty(x => x.SerialNumber, "SerialBankRef")
.KeyProperty(x => x.FinancialPeriodId, "FinancialPeriodRef");
Map(x => x.Code);
Map(x => x.Title);
Map(x => x.Comment);
Map(x => x.IsActive);
HasMany(x => x.BankBranchs).KeyColumns.Add(new[] { "SerialBankRef", "FinancialPeriodRef" });
}
}
And the BankBranch mapping code is :
public sealed class BankBranchMapping : ClassMap<BankBranch>
{
public BankBranchMapping()
{
CompositeId()
.KeyProperty(x => x.FinancialPeriodId, "FinancialPeriodRef")
.KeyProperty(x => x.SerialNumber, "SerialNumber");
Map(x => x.Code);
Map(x => x.Title);
Map(x => x.Comment);
Map(x => x.IsActive);
Map(x => x.Address);
Map(x => x.Fax);
Map(x => x.Telephone);
References(x => x.Bank).Columns(new[] { "SerialBankRef", "FinancialPeriodRef" });
}
}
As you can see the FinancialPeriodRef field acts as foreign key and part of composite key in the BankBranch mapping and NHibernate builds DB correctly and everything seems to be fine until I try to insert a record in the BankBranch table.
The error System.IndexOutOfRangeException : Invalid index 10 for this SqlParameterCollection with Count=10. which indicates I've mapped a field (FinancialPeriodRef) twice as FK and PK appears and I have no idea how to fix the problem.
I need FinancialPeriodRef in the BankBranch as part of primary key while it's absolutely equal to FinancialPeriodRef from Bank.
I need this field to establish unique constraint and also benefits of composite key is essential.
You need to use KeyReference rather than KeyProperty to describe composite foreign keys.
public BankBranchMapping()
{
CompositeId()
.KeyReference(x => x.FinancialPeriodId, "FinancialPeriodRef")
.KeyReference(x => x.SerialNumber, "SerialNumber");
...
}
I had the exact same problem as you and after an hour or so came across this post: https://stackoverflow.com/a/7997225/569662 which pointed me right.
I have two classes
public PartMap()
{
Id(x => x.ID).GeneratedBy.Guid();
HasOne(x => x.Stock)
.Cascade.All();
}
public Stock
{
Id(x => x.ID).GeneratedBy.Guid();
References(x => x.Part);
}
Part has only one stock, that's the reason i used HasOne.
The data is inserted ok, I have a part , a stock and they are saved ok.
My problem is when trying to delete I get an error that the stock foreing key to part was violated
"ORA-02292: integrity constraint (PRINERGY.FK121AD9E59966BE23) violated " .
I see the it tries to delete the part without deleting the related stock before.
How can I solve it?
It seems like a one-to-one bidirectional association. There are two ways how to map this association in NH (and FNH): primary key association and foreign key association. Your example is closer to foreign key association.
Foreign key association
public PartMap()
{
Id(x => x.Id).GeneratedBy.Guid();
References(x => x.Stock).Unique().Cascade.All();
Map(x => x.Name);
}
public StockMap()
{
Id(x => x.Id).GeneratedBy.Guid();
HasOne(x => x.Part).Cascade.All().PropertyRef("Stock");
Map(x => x.Name);
}
Primary key association
public PartMap()
{
Id(x => x.Id).GeneratedBy.Guid();
HasOne(x => x.Stock).Cascade.All();
Map(x => x.Name);
}
public StockMap()
{
Id(x => x.Id).GeneratedBy.Foreign("Part");
HasOne(x => x.Part).Constrained();
Map(x => x.Name);
}
For more details see http://fabiomaulo.blogspot.com/2010/03/conform-mapping-one-to-one.html.
I'm using FluentNHibernate and have done a many-to-many mapping but when I try to save my entity I get the following error:
NHibernate.Exceptions.GenericADOException: NHibernate.Exceptions.GenericADOException
: could not insert collection: [Test.Entities.Recipient.Groups#b6815d34-f436-4142-9b8e-1bfcbf25509e][SQL: SQL not available]
---- System.Data.SQLite.SQLiteException : Abort due to constraint violation
columns GroupId, idx are not unique
Here is my mapping:
public class GroupMap : ClassMap<Group>
{
public GroupMap()
{
Id(x => x.Id).GeneratedBy.Guid();
Map(x => x.Name);
Map(x => x.SenderName);
Map(x => x.Created);
HasManyToMany(x => x.Recipients)
.AsList()
.WithTableName("groups_recipients")
.WithParentKeyColumn("GroupId")
.WithChildKeyColumn("RecipientId")
.LazyLoad()
.Cascade.AllDeleteOrphan();
}
}
public class RecipientMap : ClassMap<Recipient>
{
public RecipientMap()
{
Id(x => x.Id).GeneratedBy.Guid();
Map(x => x.Firstname);
Map(x => x.Lastname);
Map(x => x.Phone);
Map(x => x.Email);
HasManyToMany(x => x.Groups)
.AsList()
.WithTableName("groups_recipients")
.WithParentKeyColumn("RecipientId")
.WithChildKeyColumn("GroupId")
.LazyLoad().Cascade.None();
}
}
The problem seems to have something to do with the relationship tables id but I can't figure out how to solve it.
Cheers,
nandarya
Using AsList() was not a good idea. Should be AsBag(). And everything seems to work.
I'm getting a mapping error in Fluent NHibernate. Why is it still looking for _id when I have specified the column explicitly?
Invalid column name 'Account_id'.
[GenericADOException: could not initialize a collection: [ProtoStack.Business.Entities.Account.LedgerEntries#1][SQL: SELECT ***ledgerentr0_.Account_id*** as Account5_1_, ledgerentr0_.Id as Id1_, ledgerentr0_.Id as Id43_0_, ledgerentr0_.LedgerEntryDate as LedgerEn2_43_0_, ledgerentr0_.Amount as Amount43_0_, ledgerentr0_.AccountId as AccountId43_0_ FROM dbo.LedgerEntry ledgerentr0_ WHERE ledgerentr0_.Account_id=?]]
I have explicitly specified that the column is "AccountId".
public class AccountMap : ClassMap<Account>
{
public AccountMap()
{
Table("dbo.Account");
Id(x => x.Id)
.Column("Id");
Map(x => x.Code);
Map(x => x.Name);
Map(x => x.Description);
Map(x => x.Category);
References(x => x.Group)
.Column("AccountGroupId");
HasMany(x => x.LedgerEntries)
.Inverse()
.Cascade.All();
}
}
public class LedgerEntryMap : ClassMap<LedgerEntry>
{
public LedgerEntryMap()
{
Table("dbo.LedgerEntry");
Id(x => x.Id)
.Column("Id");
References(x => x.Account)
.Column("AccountId");
Map(x => x.LedgerEntryDate);
Map(x => x.Amount);
}
}
Am I missing something?
My bad. I was missing KeyColumn.
public class AccountMap : ClassMap<Account>
{
public AccountMap()
{
Table("dbo.Account");
Id(x => x.Id)
.Column("Id");
Map(x => x.Code);
Map(x => x.Name);
Map(x => x.Description);
Map(x => x.Category);
References(x => x.Group)
.Column("AccountGroupId");
HasMany(x => x.LedgerEntries)
.KeyColumn("AccountId")
.Inverse()
.Cascade.All();
}
}
Works now.
I am trying to map several tables using Fluent Nhibernate. My tests are giving me the following error:
NHibernate.Exceptions.GenericADOException: could not initialize a collection: [FluentWeb.Domain.Employees.Orders#1]
I am trying to map a one to many relationship between Employees and Orders. Orders then has a many to many relation ship with Products (NorthWind). Here are my mappings.. Can someone give me a hand. All was working with HBMs
public class EmployeeMap : ClassMap<Employees>
{
public EmployeeMap()
{
Id(x => x.EmployeeID);
Map(x => x.FirstName);
Map(x => x.LastName);
Map(x => x.City);
Map(x => x.HireDate);
Map(x => x.Title);
HasMany(x => x.Orders)
.AsBag().WithForeignKeyConstraintName("EmployeeID")
.Inverse()
.Cascade.All();
}
}
public class OrdersMap : ClassMap<Orders>
{
public OrdersMap()
{
Id(x => x.OrderID);
Map(x => x.OrderDate);
Map(x => x.RequiredDate);
Map(x => x.ShippedDate);
References(x => x.Employee);
HasManyToMany(x => x.Products)
.Cascade.All()
.WithTableName("Order Details");
}
}
public class ProductsMap : ClassMap<Products>
{
public ProductsMap()
{
Id(x => x.ProductID);
Map(x => x.ProductName);
Map(x => x.UnitPrice);
HasManyToMany(x => x.Orders)
.Cascade.All()
.Inverse()
.WithTableName("Order Details");
}
}
Please let me know if more information is needed. Thanks for the help!
-Nick
Spaces in table names is usually a pretty bad idea. If you can't remove them, try surrounding the table names in backticks, which is NHibernate's database agnostic escaping mechanism.
.WithTableName("`Order Details`');