I'm trying to map a many-to-many collection with Fluent NHibnernate. My model class has this property:
public virtual IList<Resource> Screenshots
{
get { return _screenshots; }
protected set { _screenshots = value; }
}
And my fluent mapping is:
HasManyToMany(x => x.Screenshots)
.AsList(x => x.WithColumn("Index"))
.Cascade.AllDeleteOrphan();
When I run my application, I get the following exception message:
The element 'list' in namespace
'urn:nhibernate-mapping-2.2' has
invalid child element 'many-to-many'
in namespace
'urn:nhibernate-mapping-2.2'. List of
possible elements expected: 'index,
list-index' in namespace
'urn:nhibernate-mapping-2.2'.
There should be a way to do this. Does anyone know what I am doing wrong?
The current FluentNHibernate syntax for this is as follows:
HasManyToMany(x => x.Screenshots)
.AsList(i => i.Column("`Index`"));
The index column defaults to Index, but that's a reserved word on SQL Server (and probably other databases too), so you must quote it with back-ticks.
Also, I'd recommend against setting a cascade on this relationship. Consider the following code:
x.Screenshots.Remove(s);
session.SaveOrUpdate(x);
NHibernate will correctly delete rows from the linking table even without a cascade specified. However, if you specify AllDeleteOrphan, then NHibernate will delete the row from the linking table and also delete Resource s. I doubt this is the behavior you want on a many-to-many relationship.
Related
I've got a table called AdministratorPrivilages that has the following fields:
ID
List item
MemberId
Value
MemberType
Now, the members can be of two types (Enterprise and Express). Enterprise members live in the enterprise table. Express members live in the expressmember table. I've tried to do my fluent mapping like so.
public class AdministratorPrivilegesMapping : ClassMap<AdministratorPrivileges>
{
public AdministratorPrivilegesMapping()
{
Id(x=>x.Id);
Map(x => x.Value).Column("Value");
ReferencesAny(x => x.Member)
.EntityTypeColumn("MemberType")
.EntityIdentifierColumn("MemberId")
.IdentityType<Int32>()
.AddMetaValue<ExpressMember>("Express")
.AddMetaValue<Member>("Enterprise");
}
}
Both member tables have integer ids with ascending values. When I try to pull back the privilages associated with enterprise member 10, I'm getting the permission set associated with Express Member 10. Both other tables are mapped with the old school hbm mapping files.
Am I missing something obvious? I'm using NHibernate 2.1 and FluentNhibernate 1.1
I actually found the solution using .Where().
My situation is a little different, I have objectA and objectB.
objectA contains objectB, but objectB also contains a collection of objectBs.
"objectA":{
"someProp" : "(string)",
"objectB":{
"someProp" : "(string)",
"comeCol" : [
"(objectB)"
]
}
}
So the "Parent" property of objectB can either be of type objectB or objectA, which is why I needed to use ReferencesAny in the mapping of objectB.
Mapping looks like
ReferencesAny( x => x.Parent )
.IdentityType< int >()
.MetaType< string >()
.EntityTypeColumn( "ParentType" )
.EntityIdentifierColumn( "ParentId" )
.AddMetaValue< objectA >( "E" )
.AddMetaValue< objectB >( "S" )
.Access.Property()
.LazyLoad()
.Cascade.All();
All this works well when saving, however, my problem occured when retrieving, because the framework wasn't told what to retrieve and simply retrieved everything.
So now here is the mapping of the collection that fixed the problem:
HasMany( x => x.objectBs )
.KeyColumn( "ParentId" )
.Where( "ParentType = 'S'" )
.Cascade.All()
.LazyLoad();
Hope it will help anyone in the same situation :)
As seems to always be the case when I post on Stackoverflow, I'm being a complete goober and missing something glaringly obvious that cleared itself up with a good night's rest and some caffeine. I needed to look into the mappings for the ExpressMember and Member classes. Turns out the bag declarations there didn't filter by the type of object appropriately. Initially, I thought I had made the breaking change, when in fact it was actually a very old issue. The collision in id numbers between the two different types of members was not an issue for a very long time, as express members typically have either all the permissions or none, as do most of the older members (which were created first under an admin/plebe scheme with privileges being broken out later).
i have readonly VIEWs in an existing Database and i'd like to get them with FHN. i tried mapping it the following way:
public class HhstMap : ClassMap<Hhst>
{
public HhstMap()
{
Table("HHST");
ReadOnly();
Id();
Map(x => x.Hkz);
Map(x => x.Kapitel);
Map(x => x.Titel);
Map(x => x.Apl);
Map(x => x.Hhpz);
}
}
but i got an error:
could not execute query
[ SELECT this_.id as id3_0_, this_.Hkz as Hkz3_0_, this_.Kapitel as Kapitel3_0_, this_.Titel as Titel3_0_, this_.Apl as Apl3_0_, this_.Hhpz as Hhpz3_0_ FROM HHST this_ ]
this is ok cause there is no ID Column, but how can i do mapping with Fluent without the ID?
You could retrieve the records as value objects (non-managed entities) instead of entities.
"14.1.5. Returning non-managed entities
It is possible to apply an IResultTransformer to native sql queries. Allowing it to e.g. return non-managed entities.
sess.CreateSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
.SetResultTransformer(Transformers.AliasToBean(typeof(CatDTO)))
This query specified:
the SQL query string
a result transformer
The above query will return a list of CatDTO which has been instantiated and injected the values of NAME and BIRTHNAME into its corresponding properties or fields. "
Maybe this can help you: Fluent nHibernate no identity column in table…?
EDIT:
Also, you could use a composite id, or maybe you need the latest version of Fluent Nhibernate?
Hey guys, I am having some real issues with mapping using fluent nhibernate. I realise there are MANY posts both on this site and many others focusing on specific types of mapping but as of yet, I have not found a solution that solves my issue.
Here is what I have:
namespace MyProject.Models.Entites
{
public class Project
{
public virtual Guid Id {get; set;}
// A load of other properties
public virtual ProjectCatagory Catagory{get;set;}
}
}
and then the map:
namespace MyProject.DataAccess.ClassMappings
{
public class ProjectMap : ClassMap<Project>
{
public ProjectMap()
{
Id(x => x.Id);
Map(x => x.Title);
Map(x => x.Description);
Map(x => x.LastUpdated);
Map(x => x.ImageData).CustomSqlType("image");
HasOne(x => x.Catagory);
}
}
}
So as you can see, I have a project which contains a catagory property. Im not so hot on relational databases but from what I can figure, this is a many-one relationship where many Projects can have one catagory. No, projects cannot fall into more than one category.
So now we have:
namespace MyProject.Models.Entities
{
public class ProjectCatagory
{
public virtual Guid Id { get; set; }
public virtual String Name { get; set; }
}
}
and its map:
public ProjectCatagoryMap()
{
Id(x => x.Id);
Map(x => x.Name);
}
Issue is, well, it doesn't work ! I will do something similar to the following in a unit test:
Project myproject = new Project("Project Description");
// set the other properties
myProject.Catagory = new ProjectCatagory(Guid.New(), "Test Catagory");
repository.Save(myProject);
Now I have tried a number of mapping and database configurations when trying to get this to work. Currently, the Project database table has a column, "Catagory_id" (which i didnt put there, i assume NH added it as a result of the mapping) and I would LIKE it set to not allow nulls. However, when set as such, I get exceptions explaining that I cannot insert null values into the table (even though during a debug, i have checked all the properties on the Project object and they are NOT null).
Alternatively, I can allow the table to accept nulls into that column and it will simply save the Project object and totally disregard the Category property when saving, therefore, when being retrieved, tests to check if the right category has been associated with the project fails.
If i remember correctly, at one point I had the ProjectMap use:
References(x => x.Catagory).Column("Catagory_id").Cascade.All().Not.Nullable();
this changed the exception from "Cannot insert null values" to a foreign key violation.
I suspect the root of all this hassle comes from my lack of understanding of relational database set up as I have other entities in this project that do not have external dependencies which work absolutely fine with NHibernate, ruling out any coding issues I may of caused when creating the repository.
Any help greatly appreciated. Thank you.
The main issue here is a common misunderstand about the "one-to-one" relation in a relational database and the HasOne mapping in Fluent. The terms in the mapping are relational terms. (Fluent tries to "beautify" them a bit which makes it worse IMO. HasOne actually means: one-to-one.)
Take a look at the Fluent wiki:
HasOne is usually reserved for a
special case. Generally, you'd use a
References relationship in most
situations (see: I think you mean a
many-to-one).
The solution is very simple, just exchange HasOne with References (one-to-one to many-to-one in an XML mapping file). You get a foreign key in the database which references the ProjectCatagory.
A real one-to-one relation in a relational database is ideally mapped by a primary key synchronization. When two objects share the same primary key, then you don't waste space for additional foreign keys and it is ensured to be one-to-one.
To synchronize primary key, you need to hook up one's key to the others. However this works, it is not what you need here.
After playing around with all the available options for mapping. I found the answer to be similar to that suggested.
As was suspected, HasOne() was clearly wrong and References(x => x.Catagory) was part of the solution. However, I still received foreign key violation exceptions until:
References(x => x.Catagory).Column("Catagory_id").Cascade.SaveUpdate().Not.Nullable().Not.LazyLoad();
Just thought id update the thread in case someone else stumbles across this with a similar issue as just using References() did not work.
Its seems ProjectCatagory class is parent class of Project Class. So without parent class how can child class exist.
You have to use -
References(x => x.Catagory).Column("Catagory_id").Foreignkey("Id");
here Foreign Key is your ProjectCatagory table ID.
I am using Fluent NHibernate on a project that has an Oracle 10g database. I am also using SQLite with a schema generated from my mapping for testing. The problem I am having is that, since the primary keys on my tables are generated by Oracle Sequences, to get my Add methods to work correctly I had to add .GeneratedBy.Sequence({sequence name}) to the Id field of each of my mappings so that the mappings look something like this:
public CustomerMap()
{
Id(x => x.Id)
.Column("Cust_Id")
.GeneratedBy.Sequence("SEQ_CONTNT_AREA");
...
{More Mapping Code}
...
}
Once I added the GeneratedBy.Sequece to my mappings all of my tests began to fail giving me the following errors:
----> NHibernate.MappingException : could not instantiate id generator
----> NHibernate.MappingException : Dialect does not support sequences
These errors make sense to me since SQLite does not use Oracle Sequences for Primary Key generation. However, if I change my mapping to use GeneratedBy.Native(), all of my tests pass, but when I run the application I get the following error when trying to insert a new record:
InnerException = {System.Data.OracleClient.OracleException: ORA-02289: sequence does not exist
at System.Data.OracleClient.OracleConnection.CheckError(OciErrorHandle errorHandle, Int32 rc)
at System.Data.OracleClient.OracleCommand.Execute(OciStatementHandle stateme...
Message: "could not get next sequence value[SQL: select hibernate_sequence.nextval from dual]" string
This error message indicates to me that NHibernate either couldn't find the sequence needed to create the primary key for this table or it didn't even look for it. In either case, it appears to be trying to use 'hibernate_sequence' to get the value which it obviously cannot find since it doesn't exist.
I figure I'm probably missing something pretty simple due to my being pretty new to NHibernate. It is pretty important that I get both the tests and code working soon, as I will be handing this project over to someone else who as even less NHibernate experience than I do when I leave for my new job next week.
I know this is a huge hack, but could you do something like this?
public CustomerMap()
{
if (SomeStaticClass.IsRunningUnitTests)
{
Id(x => x.Id)
.Column("Cust_Id")
.GeneratedBy.Native();
}
else
{
Id(x => x.Id)
.Column("Cust_Id")
.GeneratedBy.Sequence("SEQ_CONTNT_AREA");
}
...
{More Mapping Code}
...
}
I am using S#arp architecture with Fluent Nhibernate and Automapper on a legacy DB.
The id column of one of the tables is different from the Automapping convention and therefore I tried to override it without success. I end up with this error
FluentNHibernate.Cfg.FluentConfigurationException
: An invalid or incomplete
configuration was used while creating
a SessionFactory. Check
PotentialReasons collection, and
InnerException for more detail.
Database was not configured
through Database method.
FluentNHibernate.Cfg.FluentConfigurationException
: An invalid or incomplete
configuration was used while creating
a SessionFactory. Check
PotentialReasons collection, and
InnerException for more detail.
Database was not configured
through Database method.
---- NHibernate.MappingException :
(XmlDocument)(3,6): XML validation
error: The element 'class' in
namespace 'urn:nhibernate-mapping-2.2'
has invalid child element 'property'
in namespace
'urn:nhibernate-mapping-2.2'. List of
possible elements expected: 'meta,
subselect, cache, synchronize,
comment, tuplizer, id, composite-id'
in namespace
'urn:nhibernate-mapping-2.2'. ----
System.Xml.Schema.XmlSchemaValidationException
: The element 'class' in namespace
'urn:nhibernate-mapping-2.2' has
invalid child element 'property' in
namespace
'urn:nhibernate-mapping-2.2'. List of
possible elements expected: 'meta,
subselect, cache, synchronize,
comment, tuplizer, id, composite-id'
in namespace
'urn:nhibernate-mapping-2.2'.
How do I user the Id automapper convention and set my custom column as the id through the override functionality?
Note: This is only for one entity. I don’t want to change the general id mapping convention
Here’s my current override function
public class AuthMap : IAutoMappingOverride<Auth>
{
public void Override(AutoMapping<Auth> mapping)
{
mapping.Table("x_auth");
mapping.Map(x => x.Id, "user_id");
mapping.Map(x => x.SessId, "sess_id");
}
}
Figured out:
Use it as
mapping.Id(x => x.Id).Column("user_id");