Fluent NH - illegal access to loading collection - nhibernate

In the CategoriesTranslated collection i have this error: illegal access to loading collection.
public class Category : Entity
{
public Category()
{
CategoriesTranslated = new List<CategoryTranslated>();
}
public virtual Category Parent { get; set; }
public virtual string Name { get; set; }
public virtual IList<CategoryTranslated> CategoriesTranslated { get; set; }
}
public class CategoryTranslated : Entity
{
public CategoryTranslated()
{
}
public virtual Category Category { get; set; }
public virtual LanguageType Language { get; set; }
public virtual string Name { get; set; }
}
public void Override(AutoMapping<Category> mapping)
{
mapping.HasMany(x => x.CategoriesTranslated)
.Inverse()
.Cascade.All();
}
public void Override(AutoMapping<CategoryTranslated> mapping)
{
mapping.References(x => x.Category);
}
The SQL:
CREATE TABLE Category(
[Id] smallint primary key identity(1,1),
[Parent] smallint null,
[Name] varchar(50) not null unique,
)
alter table [Category] add CONSTRAINT fk_Category_Category
FOREIGN KEY(Parent) references Category (Id)
go
CREATE TABLE CategoryTranslated(
[Id] smallint primary key identity(1,1),
[Category] smallint not null,
[Language] tinyint not null,
[Name] varchar(50) not null,
)
alter table [CategoryTranslated] add CONSTRAINT fk_CategoryTranslated_Category
FOREIGN KEY(Category) references Category (Id)
go
Where is it wrong?
UPDATE
The links to the hbm generater:
Category:
http://uploading.com/files/fmb71565/SubmitSiteDirectory.Core.Category.hbm.xml/
Category translated:
http://uploading.com/files/9c9aaem9/SubmitSiteDirectory.Core.CategoryTranslated.hbm.xml/

I am guessing it has to do with the creation of the list inside the constructor, especially if you left a default ctor for NHib. And that NHib is trying to set the list before it's created. The other complication here is that you have a bi-directional relationship, and CategoryTranslated may be trying to get at the list before its created too.
I doubt this is the only solution, but here is a pattern I use that should solve the error:
/// <summary>Gets the ....</summary>
/// <remarks>This is what is available to outside clients of the domain.</remarks>
public virtual IEnumerable<CategoryTranslated> CategoriesTranslated{ get { return _InternalCategoriesTranslated; } }
/// <summary>Workhorse property that maintains the set of translated categories by:
/// <item>being available to <see cref="Category"/> to maintain data integrity.</item>
/// <item>lazily instantiating the <see cref="List{T}"/> when it is needed.</item>
/// <item>being the property mapped to NHibernate, the private <see cref="_categoriesTranslated"/> field is set here.</item>
/// </list>
/// </summary>
protected internal virtual IList<Category> _InternalCategoriesTranslated
{
get { return _categoriesTranslated?? (_categoriesTranslated= new List<Category>()); }
set { _categoriesTranslated= value; }
}
private IList<StaffMember> _categoriesTranslated;
Now you need to set your mapping to access the private field, so assuming you use my casing preferences here, you'd have:
public void Override(AutoMapping<Category> mapping)
{
mapping.HasMany(x => x.CategoriesTranslated)
.Inverse()
.Access.CamelCaseField(CamelCasePrefix.Underscore)
.Cascade.All();
}
HTH,
Berryl
EDIT ============
The _Internal collection also gives the child of of the bi-directional relationship, CategoryTranslated in this case, a hook, as shown in the code below:
public virtual CategoryTranslated CategoryTranslated
{
get { return _categoryTranslated; }
set
{
if (_categoryTranslated!= null)
{
// disassociate existing relationship
_categoryTranslated._InternalCategoryTranslated.Remove(this);
}
_categoryTranslated= value;
if (value != null)
{
//make the association
_categoryTranslated._InternalCategoryTranslated.Add(this);
}
}
}
private CategoryTranslated _categoryTranslated;

Related

Fluent NHibernate automapping composite id with component

I have this complex situation: a database of countries/regions/states/cities which primary key is composed by a code (nvarchar(3)) in a column called "Id" plus all key columns of "ancestors" (regions/states/cities).
So the table country has only one key coumn (Id) while cities has 4 key columns (Id, StateId,regionId,CountryId). Obviously they're all related, so each ancestor column is a foreign key to the related table.
I have Entities in my Model that map this relationships. But they all derive from one type called Entity<T> where T may be a simple type (string, in etc) or a complex one (a component implementing the key).
Entity<T> implements a single property called Id of type T.
For each db table, if it has a comlex key, I implement it in a separate component, which oveerides also Equals and GetHashCode() Methods (in future I'll implement those in the Entity base class).
So I have a RegionKey componet that has 2 properties (Id and CountryId).
I have conventions for Foreign Key and primary key naming and type and that is ok.
I have also Mapping ovverrides for each complex Entity.
For simplicity, lets concentrate only on Countries and Regions table. Here they are:
public class Country: Entity<string>
{
public virtual string Name { get; set; }
public virtual IList<Region> Regions { get; set; }
}
public class Region: Entity<RegionKey>
{
public virtual string Name { get; set; }
public virtual Country Country { get; set; }
}
and the RegionKey component:
namespace Hell.RealHellState.Api.Entities.Keys
{
[Serializable]
public class RegionKey
{
public virtual string Id { get; set; }
public virtual string CountryId { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
var t = obj as RegionKey;
if (t == null)
return false;
return Id == t.Id && CountryId == t.CountryId;
}
public override int GetHashCode()
{
return (Id + "|" + CountryId).GetHashCode();
}
}
}
Here is the configuration of AutoPersistenceModel:
public ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(
MsSqlCeConfiguration.Standard
.ConnectionString(x=>x.Is(_connectionString))
)
.Mappings(m => m.AutoMappings.Add(AutoMappings))
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}
private AutoPersistenceModel AutoMappings()
{
return AutoMap.Assembly(typeof (Country).Assembly)
.IgnoreBase(typeof(Entity<>))
.Conventions.AddFromAssemblyOf<DataFacility>()
.UseOverridesFromAssembly(GetType().Assembly)
.Where(type => type.Namespace.EndsWith("Entities"));
}
private static void BuildSchema(Configuration config)
{
//Creates database structure
new SchemaExport(config).Create(false, true);
//new SchemaUpdate(config).Execute(false, true);
}
Here is the Regions entity overrides
public class RegionMappingOverride : IAutoMappingOverride<Region>
{
public void Override(AutoMapping<Region> mapping)
{
mapping.CompositeId(x=>x.Id)
.KeyProperty(x => x.Id, x=> x.ColumnName("Id").Length(3).Type(typeof(string)))
.KeyProperty(x => x.CountryId, x => x.ColumnName("CountryId").Length(3).Type(typeof(string)));
}
}
Ok now when I test this mapping I got an error saying: The data types of the columns in the relationship do not match.
I have also tried this override:
public void Override(AutoMapping<Region> mapping)
{
mapping.CompositeId()
.ComponentCompositeIdentifier(x=>x.Id)
.KeyProperty(x => x.Id.Id, x=> x.ColumnName("Id").Length(3).Type(typeof(string)))
.KeyProperty(x => x.Id.CountryId, x => x.ColumnName("CountryId").Length(3).Type(typeof(string)));
}
And it almost work but it creates a Regions table with a single column key of varbinary(8000) which is not what I want:
CREATE TABLE [hell_Regions] (
[Id] varbinary(8000) NOT NULL
, [Name] nvarchar(50) NULL
, [CountryId] nvarchar(3) NULL
);
GO
ALTER TABLE [hell_Regions] ADD CONSTRAINT [PK__hell_Regions__0000000000000153] PRIMARY KEY ([Id]);
GO
ALTER TABLE [hell_Regions] ADD CONSTRAINT [FK_Regions_Country] FOREIGN KEY ([CountryId]) REFERENCES [hell_Countries]([Id]) ON DELETE NO ACTION ON UPDATE NO ACTION;
GO
I don't have a clue of how to deal with it since it seems to me everythin is ok.
Thanks in advance for your answers
Ok I menaged to solve it: I had to sign the CompositeId class as MAPPED, since it is a component. So this is my new RegionMappingOverride:
public class RegionMappingOverride : IAutoMappingOverride<Region>
{
public void Override(AutoMapping<Region> mapping)
{
mapping.CompositeId(x=>x.Id)
.Mapped()
.KeyProperty(x =>x.Id,x=>x.Length(3))
.KeyProperty(x => x.CountryId, x=>x.Length(3));
}
}
Now the sql created is correct:
create table hell_Countries (
Id NVARCHAR(3) not null,
Name NVARCHAR(50) null,
primary key (Id)
)
create table hell_Regions (
Id NVARCHAR(3) not null,
CountryId NVARCHAR(3) not null,
Name NVARCHAR(50) null,
primary key (Id, CountryId)
)
alter table hell_Regions
add constraint FK_Region_Country
foreign key (CountryId)
references hell_Countries

Make AskedBy_PersonId the foreign column name(instead of the default AskedBy_id) on Fluent NHibernate

I have these tables:
create table Person
(
PersonId int identity(1,1) primary key,
PersonName nvarchar(100) not null
);
create table Question
(
QuestionId int identity(1,1) primary key,
QuestionText nvarchar(100) not null,
AskedBy_PersonId int not null references Person(PersonId),
QuestionModifiedBy_PersonId int not null references Person(PersonId)
);
And I have these models:
public class Question
{
public virtual int QuestionId { get; set; }
public virtual string QuestionText { get; set; }
public virtual Person AskedBy { get; set; }
public virtual Person QuestionModifiedBy { get; set; }
}
public class Person
{
public virtual int PersonId { get; set; }
public virtual string PersonName { get; set; }
}
I'm using automapping with FluentNHibernate, the referencing properties defaults to these database column names:
AskedBy_id
QuestionModifiedBy_id
How can one make FluentNHibernate make the referencing properties be mapped to this style of foreign column name?
AskedBy_PersonId
QuestionModifiedBy_PersonId
As of now, I'm doing it in manual overriding:
.Override<Question>(x =>
{
x.References(y => y.AskedBy).Column("AskedBy_PersonId");
x.References(y => y.QuestionModifiedBy).Column("QuestionModifiedBy_PersonId");
})
I wanted to remove that overriding and want Fluent NHibernate to automatically make the foreign column name follow the naming pattern above
How can I achieved that with Fluent NHibernate?
Should be easy with IReferenceConvention implementation:
public class ReferenceConvention : IReferenceConvention
{
public void Apply(IManyToOneInstance instance)
{
instance.Column(
instance.Name + "_" + instance.Property.PropertyType.Name + "Id");
}
}
NHibernate should be configured to read the conventions (with something like this):
Fluently.Configure()
//... other configuration
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<Person>()
.Conventions.AddFromAssemblyOf<ReferenceConvention>());

HNibernate 1 to Many Relationship Fluent NHiberate using only the Foreign Key Id

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);
}
}

One-to-one mapping in S#arp Architecture

There's a distinct smell of burned out circuits coming from my head, so forgive my ignorance.
I'm trying to setup a one-to-one relationship (well, let Automapper do it) in S#arp Architecture.
I have
public class User : Entity
{
public virtual Profile Profile { get; set; }
public virtual Basket Basket { get; set; }
public virtual IList<Order> Orders { get; set; }
public virtual IList<Role> Roles { get; set; }
...
}
public class Basket : Entity
{
public virtual User User { get; set; }
...
}
public class Profile : Entity
{
public virtual User User { get; set; }
...
}
And my db schema is
CREATE TABLE [dbo].[Users](
[Id] [int] IDENTITY(1,1) NOT NULL,
...
CREATE TABLE [dbo].[Profiles](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserFk] [int] NULL,
...
CREATE TABLE [dbo].[Baskets](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserFk] [int] NULL,
...
When I run the unit test CanConfirmDatabaseMatchesMappings in MappingIntegrationTests I get the following error
NHibernate.ADOException : could not
execute query ...
System.Data.SqlClient.SqlException :
Invalid column name 'ProfileFk'.
Invalid column name 'BasketFk'.
and the sql it's trying to execute is
SELECT TOP 0
this_.Id AS Id6_1_ ,
..
user2_.ProfileFk AS ProfileFk9_0_ ,
user2_.BasketFk AS BasketFk9_0_
FROM
Profiles this_
LEFT OUTER JOIN Users user2_
ON this_.UserFk = user2_.Id
So it's looking for a ProfileFk and BasketFk field in the Users table.
I haven't setup any customer override mappings and as far as I can see I've followed the default conventions setup in S#.
The two other mappings for IList Orders and Roles seem to map fine. So I'm guessing that it've missed something for setting up a one-to-one relationship.
What am I missing?
Got it. This is really an NHibernate problem to solve with Fluent NHibernate syntax, but it happens to be relevant for S#.
Background reading: NHibernate Mapping and Fluent NHibernate HasOne
What you do is override the mapping for User and give it two .HasOne mappings. Then set a unique reference to the user on the Profile and Basket class:
public class UserMap : IAutoMappingOverride<User>
{
#region Implementation of IAutoMappingOverride<User>
/// <summary>
/// Alter the automapping for this type
/// </summary>
/// <param name="mapping">Automapping</param>
public void Override(AutoMapping<User> mapping)
{
mapping.HasOne(u => u.Profile);
mapping.HasOne(u => u.Basket);
}
#endregion
}
public class ProfileMap : IAutoMappingOverride<Profile>
{
#region Implementation of IAutoMappingOverride<Profile>
/// <summary>
/// Alter the automapping for this type
/// </summary>
/// <param name="mapping">Automapping</param>
public void Override(AutoMapping<Profile> mapping)
{
mapping.References(p => p.User).Unique().Column("UserFk");
}
#endregion
}
public class BasketMap : IAutoMappingOverride<Basket>
{
#region Implementation of IAutoMappingOverride<Basket>
/// <summary>
/// Alter the automapping for this type
/// </summary>
/// <param name="mapping">Automapping</param>
public void Override(AutoMapping<Basket> mapping)
{
mapping.References(b => b.User).Unique().Column("UserFk");
}
#endregion
}
As a side note, at the time of writing this, NHibernate 3 has just been released. There's a great book out called NHibernate 3.0 Cookbook which I've just bought and it looks extremely useful for working with S#.

Specify a Fluent NHibernate automapping to add a unique constraint to all entities

My automapping:
return Fluently.Configure()
.Database(config)
.Mappings(m =>
m.AutoMappings.Add(
AutoMap.AssemblyOf<Company>()
.Where(
t => t.Namespace == "DAL.DomainModel" && t.IsClass)
.IgnoreBase<ReferenceEntity>()))
.BuildSessionFactory();
So ReferenceEntity is an abstract class containing a string Name, and all my reference entities inherit from this class. I'd like to modify my automapping to add a unique constraint to the Name field for all entities that inherit from ReferenceEntity.
I've gathered it has something to do with .Setup but I'm a bit lost on how to proceed.
note: I'm using the Fluent NHibernate v1.0 RTM so conventions will be with the new style if that is relavent to my goal.
If all your entities inherit from ReferenceEntity, wouldn't you want to create the unique constraint for the Name property on all the entities that are mapped?
But if you want to filter by entity base class, you can do it. Use a convention to add the unique constraint to your mappings:
public class NameConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
// Check the entity base class type
if (instance.EntityType.BaseType.Name == "ReferenceEntity")
{
// Only add constraint to the .Name property
if (instance.Name == "Name")
{
instance.Unique();
}
}
}
}
To get the convention (and all other conventions in the assembly) picked up by FNH, just add this line the AutoMap setup you have above:
.Conventions.AddFromAssemblyOf<NameConvention>()
Alex,
No the answer doesn't change. Here is an example, using the convention above.
public abstract class ReferenceEntity
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
}
public class User : ReferenceEntity
{
public virtual string Email { get; set; }
}
public class Item : ReferenceEntity
{
public virtual string Description { get; set; }
}
This creates sql of:
create table Users (
Id INTEGER not null,
Email TEXT not null,
Name TEXT not null unique,
primary key (Id)
)
create table Items (
Id INTEGER not null,
Description TEXT,
Name TEXT not null unique,
primary key (Id)
)
As long as these are separate entities, it will create a unique constraint on the .Name property for each entity.