How to add a additional field in the database schema(SQL) from NHibernate mapping exported that not exist on entity?
I have:
Property(x => x.Name, "Name");
Property(x => x.Description, "Product");
and I want add to exported schema(SQL):
Property("Department"); (this property not exist in the entity)
You should be able to use an AuxilliaryDatabaseObject.
I'm fairly certain that Nhibernate is not going to handle this scenario. Nhibernate can map to an existing database in a database first scenario or generate the schema in a domain first scenario. Nhibernate only knows about the database objects that you tell it about in the mapping. Since you've got extra fields on tables it sounds like you've got a database first scenario and should use something like database version software.
Related
I'm trying to get a many to many relationship to work using Fluent Nhibernate.
I have a Product and a RelatedProduct Table
Product {Id, Name...}
ProductRelated{Id, ProductId, RelatedProductId, relOrder}
and a Product class
The mapping looks like
HasManyToMany(x => x.RelatedProducts)
.Table("ProductRelated")
.ReadOnly()
.ChildOrderBy("relOrder asc")
.ParentKeyColumn("ProductId")
.ChildKeyColumn("RelatedProductId");
When a query is done for Product and the RelatedProducts are lazy loaded I can see that the sorting is applied correctly using the relOrder on the join table.
Session.Query<Product>()
.FetchMany(p => p.Categories)
.FetchMany(p => p.Departments)
Once I add in eager loading of the related products NHibernate tries to sort by a relOrder column on the product itself instead of on the join table.
Session.Query<Product>()
.FetchMany(p => p.Categories)
.FetchMany(p => p.Departments)
.FetchMany(p => p.RelatedProducts)
Any ideas of whats going on here?
Well to answer your question what's going on here?, I would say, you are using: "not-together fitting features" of NHibernate.
A snippet from documentation 6.6. Sorted Collections:
Setting the order-by attribute tells NHibernate ...
Note: that lookup operations on these collections are very slow if they contain more than a few elements.
Note: that the value of the order-by attribute is an SQL ordering, not a HQL ordering!
So, this could be applied only for "standard" lazy loading, becuase this kind of a feature is applied only on a DB side. It is not managing order in the memory.
And the eager fetching, as the counter-part, is a different way how to generate and issue the SQL Statement to DB.
So, eager and order-by will never work together.
*
My NOTE: I simply have to append this. I can't help myself
I.
The Eager fetching is the feature which should be avoided (I never use it, but it's me). There is a better solution and it is setting the BatchSize(), which will reduce the 1+N into 1+(a few) and will keep all the (lazy) featrues, including order-by. Check these if interested:
NHibernate QueryOver with Fetch resulting multiple sql queries and db hits
Is this the right way to eager load child collections in NHibernate
https://stackoverflow.com/questions/18419988/
BatchSize() is supported for HasManyToMany as well: ToManyBase:
/// <summary>Specify the select batch size </summary>
/// <param name="size">Batch size</param>
public T BatchSize(int size) { ...
II.
The many-to-many mapping, while fancy at first look, is not the way I'd suggest. Try to rethink your model and introduce the first-level-citizen: PairingEntity - for the pairing object. It will then use many-to-one and one-to-many mapping which could give us more... e.g. improved querying like Subqueries... try to check these:
How to create NHibernate HasManyToMany relation
many-to-many with extra columns nhibernate
Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships?
i am trying to turn off lazyloading for one-to-many mapping in NHibernate. I have the follow mapping in my entity mapping class. An entity has many addresses, and what I was looking for is one query that basically join the base table to the Addresses table and return me all the result in one request. Instead I see a series of sql query submitted to database for each record in the base table.
HasMany(m => m.Addresses).Not.LazyLoad().Fetch.Join();
i need a way to turn off lazyloading completely.
I would strongly suggest to read this blog post by Ayende: NHibernate is lazy, just live with it.
Using ORM and trying to avoid laziness... won't work. In case of addresses you will lose paging for example.
(while fetching them via join, what happens? If there will be entity with 10
addresses, and you will ask for first 10 records... you will get just
one. And it could be worse if you will ask for 11...)
But what you can use, is the power of NHibernate: 19.1.5. Using batch fetching
HasMany(m => m.Address)
...
.Fetch.Select()
.BatchSize(25)
Now, if you will need 25 records, there will be 2 SELECTs. First for entity, second for all the related Addresses. That's improvement, while all the advantages of ORM remain.
I think that would be anough:
HasMany(m => m.Addresses).Not.LazyLoad();
To get the data with select you should explicitly use "Fetch":
session.QueryOver<Item>()
.Fetch(item => item.Addresses).Eager
.Take(1000)
.TransformUsing(Transformers.DistinctRootEntity)
.List();
I assume you load the base entity with a HQL, Linq or plain SQL query. Those queries ignore the "join" fetch settings in the mapping. You have to either explicitly fetch the Adresses in the query or use Get/Criteria/QueryOver.
Reference documentation: http://nhibernate.info/doc/nh/en/index.html#performance-fetching-custom
I have a [User] table/class and a [Company] table/class and there is a link-table [UserCompany] between them.
When editing a User, beside basic information people also could change that user's access Companies, so I do the map like this in UserMap.cs:
HasManyToMany(u => u
.Companies)
.Cascade.SaveUpdate()
.Table("UserCompany")
.ParentKeyColumn("UserId")
.ChildKeyColumn("CompanyCode")
.Not.LazyLoad();
Also in CompanyMap.cs I set inverse like this:
HasManyToMany(c => c.Users)
.Inverse()
.Table("UserCompany")
.ParentKeyColumn("CompanyCode")
.ChildKeyColumn("UserId");
The problem now is: I could update [User] information/table, plus the linking data in [UserCompany] table. However, the Fluent Nhibernate also update the [Company] table which I don't need at all.
Is there any way I could let FN not update Company table?
To stop cascading updates just remove the
.Cascade.SaveUpdate()
from your Many-to-many mapping.
It could be a bit confusing. In comparison with the cascading used on <list>s and <map>s. In that case, the update is done directly on the child table (parent has more children... child contains the ParentId - cascade is reasonable to do operation on the child record)
But here we are working with a pair table. The relation is stored there. And this table will be always managed by NHibernate (implicit cascading).
The setting .Cascade.SaveUpdate() goes to other end of the many-to-many relation. To company table in our case. It could be handy.. but you can omit that and get everything running as expected.
I have three table -
1. Anomaly
2. Markup
3. Anomaly_Markup
Mapping -
public AnomalyMap()
{
Table("anomaly");
Id(x => x.Id).Column("ID").CustomType("decimal");
HasManyToMany<DMMarkupData>(x => x.DMMarkupData)
.Table("anomaly_markup")
.ParentKeyColumn("ANOMALY_ID")
.ChildKeyColumn("MARK_UP_ID")
.Cascade.All()
.LazyLoad();
}
public MarkupDataMap()
{
Table("markup");
Id(x => x.Id).Column("ID");
}
Condition :
Save data by Anomaly - Anomaly contains MarkupData. It saves data. It is working functionality with me.
Delete markup - which should delete relationship from map table and markup data. I am facing this issue.
Anyone help me to find out solution, how to delete markup data ?
I see no relationship of MarkUpData with Anomaly. There must be same relationship and you should specify the control of cascade operation by using Inverse attribute in your mapping.
You can refer : How to set "cascade delete" option to "Set Null" in Fluent NHibernate?
To delete DMMarkupData just remove the object from collection and call for Save Anomaly.
From what you've posted it looks like you haven't defined a relationship on DMMarkupData -> Anomaly, so NHibernate won't know to delete it the MarkupData entries from the anomaly_markup table (despite the reverse relationship being there). You can either solve it with a database level cascade which removes entries in anomaly_markup when deleting MarkupData, or you can map the relationship in code & NHibernate and then NHibernate will do the cascade for you.
NHibernate does not manage the object graph, it only persists it. Removing the item from the list when it gets deleted is responsibility of the business logic!
(All the tricks with triggers and stuff that workaround it lead to inconsistencies and side effects within the transaction which does the change. From point of view of persistence ignorance it is not recommended. I would only do it when facing performance issues that can't be solved in another way.)
You can simplify it by using components. Provided that
you don't reference the same markup from somewhere else
you don't need to query for markup unrelated to Anomality
markups do never live outside of an Anomality
given all that, it is much easier to work with components. (I don't know how it is called in fluent, but in xml mapping it is called a "composite-element").
When using components, you don't need to delete the markup from the database. You just remove it from the list where it lives in.
i'm working w/ a legacy database is set-up stupidly with an index composed of a char id column and two char columns which make up a date and time, respectively. I have created a ICompositeUserTypefor the date and time columns to map to a single .NET DateTime property on my entity, which works by itself when not part of the id. i need to somehow use a composite id with key property mapping that includes my ICompositeUserType for mapping to the two char date and time columns. Apparently w/ my version of Fluent NHibernate, CompositeIdentityPart doesn't have a CustomTypeIs() method, so i can't just do the following in my override:
mapping.UseCompositeId()
.WithKeyProperty(x => x.Id, CommonDatabaseFieldNames.Id)
.WithKeyProperty(x => x.FileCreationDateTime)
.CustomTypeIs<FileCreationDateTimeType>();
is something like this even possible w/ NHibernate let alone Fluent? I haven't been able to find anything on this.
UPDATE:
after reading the NHibernate documentation more carefully, i found out that NHibernate does support component composite IDs, but it doesn't look like it supports custom types (i.e. IUserType and ICompositeUserType) for composite IDs. Also, the version of Fluent i have doesn't have support for NHibernate's support of component composite IDs (see issue 387 and issue 346). i will probably try and upgrade my entire solution, see if it works, and post an answer to my own question if it does... unless someone can tell me w/o me having to do that... 8o)
Try adding this
mapping.Map(x => x.FileCreationDateTime).Columns.Add("FileCreationDate", "FileCreationTime").CustomSqlType("YourType");
Although I am not sure what sense CustomSqlType does when referring to two columns at once. Probably none. Can you create a view that projects these to columns to normal DateTime?
NHibernate doesn't support the use of IUserType or ICompositeUserTypes for composite IDs. It does, however, support component composite IDs, but Fluent NHibernate has limited support for this. From what i could see in the source code, it doesn't allow you to specify the column names for the component properties like NHibernate allows. I had to give-up for now and just create two string fields that the two string date fields in the database could map to seperately, and then have a DateTime? property that translated the two string properties. Props to HeavyWave for their comment: "Although, I think you should just map each column to its own field and provide additional property that combines them."
mapping.CompositeId()
.KeyProperty(x => x.Id, CommonDatabaseFieldNames.Id)
.KeyProperty(x => x.FileCreationDateString, CommonDatabaseFieldNames.FileCreationDate)
.KeyProperty(x => x.FileCreationTimeString, CommonDatabaseFieldNames.FileCreationTime);