I have this in my entity:
public virtual Iesi.Collections.Generic.ISet<long> Blas { get; set; }
and this for my mapping:
mapping.HasMany(x => x.Blas).AsSet().Element("Value", m => m.Type<long>());
This creates the relevant tables and I add data like this:
X.Blas = new Iesi.Collections.Generic.HashedSet<long>();
X.Blas.Add(some_long);
This adds values to the object but the values in Blas are never persisted (everything else of X is).
Can anyone see anything wrong?
Thanks.
Christian
if X is loaded through a session then blas is initialized with a changetracking collection. So dont overwrite it. Try X.Blas.Clear(); instead of X.Blas = new Iesi.Collections.Generic.HashedSet<long>();
Try adding a cascade setting.
mapping.HasMany(x => x.Blas).AsSet()
.Element("Value", m => m.Type<long>())
.Cascade.AllDeleteOrphan();
Also you should just be able to use ICollection and a regular Hashset instead of Iesi. Provided you're using at least version 3 (it might work with 2.1.2 or higher as well)
You should follow proper object oriented encapsulation to avoid problems like this, an example in my post here: How do I map a collection accessed through a read only property?
Related
I have an entity with a binary column that's set to lazy load in the mapping. In some cases, though, we want to get the entity along with the binary data at the same time. I tried using Linq.Fetch(x => x.BinaryData), but that gives an invalid join exception. Understandable, considering it shouldn't be a join in the first place. Is there a way to get this working? I'm using NHibernate 3.1
This is the mapping:
Map(x => x.BinaryData)
.CustomSqlType("image")
.Length(int.MaxValue)
.Not.Nullable()
.LazyLoad(); // Wanna make sure we don't kill the app by loading the image data when we don't need it.
This is the fetching:
Linq.Where(x => x.Id == id).Fetch(x => x.BinaryData).FirstOrDefault();
This looks like to be not possible at the moment : https://nhibernate.jira.com/browse/NH-2888
So, You have to use HQL :
var post = session.CreateQuery("from Post fetch all properties")
.SetMaxResults(1)
.UniqueResult<Post>();
Source : http://ayende.com/blog/4377/nhibernate-new-feature-lazy-properties
In HQL you can use fetch all properties to eagerly load lazy property. But in NH3.1 it is not yet implemented for Linq queries. As I know this bug is in NHibernate Jira so you can check if it is resolved or you can fix it yourself. For our company prototype i fixed this bug, but I did so in very brute-force way so i didn't send patch to NHibernate project
I have just started to use NHibernate 3.2 with its new Conformist API, having used previous versions with Fluent a while back. The basic stuff seems fine but I am currently struggling with trying to map a string to a custom type.
In this specific case, I have a string which is a semi-colon separated list of roles in a column on one of my tables. When I get it out, I want it to be mapped into a "RoleSet" custom object that I have created by passing the string value from the database into its constructor.
I have created a IUserType but I cannot see how to tell it to use it.
Previously with Fluent I would have done this in my map class:
Map(x => x.Roles).CustomType<RoleSetType>();
Is there an equivalent way to do this is in the new API?
Give this a try...
Property(x => x.Roles, x => x.Type(typeof(RoleSetType), null));
I have a couple questions.
I been reading in nhibernate beginners guide 3 about using auto mapper with fluent. I read about this before(and I use auto mapper in my project already) however I am still not sure about a couple things.
What happens when you need to put like Not.Null(), or have to set a length, or inverse on something. How do you set those up? Won't you have to setup auto mapper for each of these properties that have these settings? Won't that sort of default the purpose?
I also been reading about common mistakes and one mistake was talking about when you need readonly. I am actually suffering from this problem and had to make a fix that I was never that happy about.
From reading this I am wondering if it is wise to have 2 mappings of these classes that I need to have readonly
Say I have this
public PlanMap()
{
Table("Plans");
Id(x => x.Id);
Map(x => x.Name).Not.Nullable().NvarcharWithMaxSize();
Map(x => x.Description).Not.Nullable().NvarcharWithMaxSize();
Map(x => x.Price).Not.Nullable();
Map(x => x.Discount).Not.Nullable();
Map(x => x.LengthInMonths).Not.Nullable();
References(x => x.Role).Not.Nullable();
HasMany(x => x.Students).Cascade.All();
}
So would it be wise to have that and then have
public ReadOnlyPlanMap()
{
Table("Plans");
ReadOnly();
SchemaAction.None();
Id(x => x.Id);
Map(x => x.Name).Not.Nullable().NvarcharWithMaxSize();
Map(x => x.Description).Not.Nullable().NvarcharWithMaxSize();
Map(x => x.Price).Not.Nullable();
Map(x => x.Discount).Not.Nullable();
Map(x => x.LengthInMonths).Not.Nullable();
References(x => x.Role).Not.Nullable();
HasMany(x => x.Students).Cascade.All();
}
Then when I need ReadOnly I use that mapping when I don't I use the other mapping? The only thing I see wrong about this is duplicate code. I am not sure if I can use inheritance or something to solve that problem though.
3.I read in the book that it recommend not to use the "auto" incrementing option in your database but instead use a hi-lo one setup in nhibernate to handle this.
In the book it says if you did something like session.Save(object); it would actually go and contact the server and this would break the unit of work. Does this happen when "auto" incrementing is set on the database? I never saw evidence of that happening and actually I had to commit a record before I would actually see the id.
When you use hi-lo what datatype does your column have to be? I usually use for my pk a incrementing int. Can I still use a int?
Finally from alot of examples I seen they usually make their PK properties like this
public virtual int Id { get; private set; }
Yet in the book I constantly saw
public virtual int Id { get; set; }
I thought using private set was the way to go to stop people from making their own number for the PK.
You would have better luck getting all of your questions answered if you broke them up into separate questions, but I'll address a couple of your questions anyway:
Automapping, Custom Conventions, and Overrides
If one of your business requirements is that most properties should not be nullable then you should make that the default by providing your own convention to the automapper. Take a look at this blog post for how you can do this.
Then if you have a mapping that needs to differ slightly from your conventions, then you can provide an automapping override by implementing IAutoMappingOverride<T> where in you only specify the columns/ids/relationships that are aberrant to the conventions.
The documentation at the FluentNHibernate wiki on Overrides and Conventions is actually quite good, I highly recommend reading it.
Readonly Entities
If I was doing this, what I'd do is have an NHibernate ignored, internal set property called something like IsReadonly { get; internal set; }, when an object is retrieved from somewhere that it should be read-only, then set that property before returning it to the caller.
If you have an explicit Save method on a repository, you can check that property and not do the actual NHibernate save if it's true. If you rely on the NHibernate dirty checking for saving on session Flush then you could implement an NHibernate listener which would not save the entity if that property was true.
Identifiers
One word (acronym) GUID; hi-lo can work, but it can get complicated and a bit finicky. For NHibernate to properly track the object it has to have a unique ID. If you're using auto ids then NHibernate will go to the database to get an ID when you Save your entity and before you do the Flush.
Comb GUIDs solve the problems that you'll run into with auto ids and hi-lo in exchange for taking a little bit more space in your DB, and memory. When using FluentNHibernate automapping, if your entity has a GUID as the type of it's Id property, it will automatically use the Guid Comb strategy.
I have the following model which I have created and mapped with nHibernate.
Using lazy loading so I don't need to get the Vehicles for the Dealer at the start.
Public class Dealer
{
public virtual string Name { get;set;}
public virtual IList<Vehicles> Vehicles { get;set;}
}
Now let's assume the Dealer has thousands of vehicles.
If I do Dealer.Vehicles.Count then NH will select and pull all the data.
What is the best way to simply get a count? Is there any way in which I can get a count with out declaring A new property dealerCount within the Dealer Class?
Also there is a feature in Hibernate which I believe will be implemented in a newer version of NH called Extra Lazy Loading. Would this solve the problem?
extra lazy loading would issue sql instead of populating the collection for certain operations such as Count or Contains. In fluent mappings its used as:
HasMany(x => x.CollectionProperty).ExtraLazyLoad();
or HBM
<one-to-many lazy="extra" ...
It's only usefull if you have large collections and need the special behavior.
Use count projection (Projections.RowCount)
Does anybody know how I would map an entity with two many-to-many collections of the same child type.
My database structure is this....
The "normal" relationship will be....
tbl_Parent
col_Parent_ID
tbl_Parent_Child_Xref
col_Parent_ID
col_Child_ID
tbl_Child
col_Child_ID
The alternative relationship is...
tbl_Parent
col_Parent_ID
tbl_Include_ParentChild_Xref
col_Parent_ID
col_Child_ID
tbl_Child
col_Child_ID
The entity and mapping look like this...
public partial class ParentEntity : AuditableDataEntity<ParentEntity>
{
public virtual IList<ChildEntity> Children { get; set; }
public virtual IList<ChildEntity> IncludedChildren { get; set; }
}
public partial class ParentMap : IAutoMappingOverride<ParentEntity>
{
public void Override(AutoMapping<ParentEntity> mapping)
{
mapping.Table("tbl_Parent");
mapping.HasManyToMany(x => x.Children)
.Table("tbl_Parent_Child_Xref")
.ParentKeyColumn("col_Parent_ID")
.ChildKeyColumn("col_Child_ID")
.Inverse()
.Cascade.All();
mapping.HasManyToMany(x => x.IncludedChildren)
.Table("tbl_Include_ParentChild_Xref")
.ParentKeyColumn("col_Parent_ID")
.ChildKeyColumn("col_Child_ID")
.Inverse()
.Cascade.All();
}
}
The error that I'm getting is
"System.NotSupportedException: Can't figure out what the other side of the many-to-many property 'Children' should be."
I'm using NHibernate 2.1.2, FluentNhibernate 1.0.
It seems FNH is confused because you seem to map the same object (ChildEntity) to two different tables, if I'm not mistaken.
If you don't really need the two lists to get separated, perhaps using a discriminating value for each of your lists would solve the problem. Your first ChildEntity list would bind to the discriminationg value A, and you sesond to the discriminating value B, for instance.
Otherwise, I would perhaps opt for a derived class of your ChildEntity, just not to have the same name of ChildEntity.
IList<ChildEntity> ChildEntities
IList<IncludedChildEntity> IncludedChildEntities
And both your objects classes would be identitical.
If you say it works with NH, then it might be a bug as already stated. However, you may mix both XML mappings and AutoMapping with FNH. So, if it does work in NH, this would perhaps be my preference. But think this workaround should do it.
You know I'm just shooting in the dark here, but it almost sounds like your ChildEntity class isn't known by Hibernate .. that's typically where I've seen that sort of message. Hibernate inspects your class and sees this referenced class (ChildEntity in this case) that id doesn't know about.
Maybe you've moved on and found the issue at this point, but thought I'd see anyway.
Fluent is confused because you are referencing the same parent column twice. That is a no-no. And as far as I can tell from the activity i have seen, a fix is not coming any time soon.
You would have to write some custom extensions to get that working, if it is possible.
To my great pity, NHibernate cannot do that. Consider using another ORM.