I have this table mapping (details don't really matter I think):
WithTable("COPACKER_FACILITY");
Id(x => x.FacilityNumber, "FACILITY_NUM").GeneratedBy.Sequence("FACSEQ");
Map(x => x.FacilityName, "FACILITY_NAME").Not.Nullable().Trimmed();
Map(x => x.AddressLine1, "ADDR1").Not.Nullable().Trimmed();
...
WithTable("FACIL_OTH_AUDIT_INFO", m =>
{
m.WithKeyColumn("FACILITY_NUM");
m.Map(x => x.ProdnShiftsNum, "PRODN_SHIFTS_NUM").Not.Nullable();
m.Map(x => x.ProdnCapacity, "PRODN_CAPACITY").Not.Nullable();
m.Map(x => x.ProdnLinesNum, "PRODN_LINES_NUM").Not.Nullable();
m.Map(x => x.AuditScore, "AUDIT_SCORE");
m.References(x => x.FacilStatus, "STATUS_IND").Not.Nullable();
});
HasMany(x => x.ComplianceFlags)
.KeyColumnNames.Add("FACILITY_NUM")
.Inverse()
.Cascade.All();
...
The reason for the one to one table is for audit reasons. There's a FACIL_OTH_AUDIT_INFO_HIST table that should get a record for every insert and update in the main table.
My question: How can I know when an insert or update happens in that table so I know to insert an audit record?
Many thanks!
+1 to what kvalcanti said... here's another post that I think explains it a little better though (and shows you how to do it without XML configuration!). I'm doing what this guy is doing on my project and it's working really well.
http://www.codinginstinct.com/2008/04/nhibernate-20-events-and-listeners.html
Caveat: I'm not inserting new objects that need to be saved in this event in my project, which I assume will not be a problem, but I can't say for sure since I'm not doing exactly what you're doing.
I posted the final solution to this problem and thought I'd share
http://robtennyson.us/post/2009/08/23/NHibernate-Interceptors.aspx
You can use event listeners.
try http://nhibernate.info/doc/howto/various/creating-an-audit-log-using-nhibernate-events.html
Related
I have Customer and Profile classes, where one Customer can have many Profiles.
I am using following NHibernate override classes with them:
public void Override(AutoMapping<Customer> mapping)
{
mapping.Table("[Customer]");
mapping.Id(x => x.Id, "Id").GeneratedBy.Identity();
mapping.HasMany(x => x.Profiles).Cascade.All().Inverse();
mapping.Map(x => x.FirstName, "FirstName");
mapping.Map(x => x.LastName, "LastName");
mapping.Map(x => x.Email, "Email");
}
public void Override(AutoMapping<Profile> mapping)
{
mapping.Table("[Profile]");
mapping.Id(x => x.Id, "Id").GeneratedBy.Identity();
mapping.References(x => x.Customer, "Customer_Id").Cascade.None();
mapping.Map(x => x.FacebookProfileLink, "FacebookProfileLink");
mapping.Map(x => x.ContactPhone, "ContactPhone");
}
I am getting following error while inserting Profile object:
Cannot insert the value NULL into column 'Customer_Id', table 'dbo.Profile'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated.
My intence is to insert Customer object before Profile that needs a reference to Customer
object. That's why I'm using Inverse attribute. Unfortunately it doesn't work. Any help on this ? Thank you.
One thing that NHibernate does as a good ORM is to save everything in the relationships so you don't have to save things separately. I think your issue might but in what you are doing when you say 'insert Customer object before Profile that needs a reference to Customer'.
The reality is that you should create the customer, with the profiles needed to be associated to it, and then just save the customer. NHibernate will save all the entities in the right order to make sure the relationships are preserved. Try doing that, first create or retrieve the customer entity, then add/remove the profiles, and then save the customer. Profiles would be saved because of the cascade option you have specified in the mapping.
Hope that helps!
I found out solution that works for me.
I am saving Customer entity without reference to it's Profiles. Afterwards I'm saving Profile entity.
Works for me even better than solution i wanted to achieve before, as I can check on success of Customer insert and then decide what to do next (save Profile as well or do some validation error).
Thank you all for your answers.
I’m wondering if I have mis-understood how the L2 caching works. I am trying to cache a ‘HasMany(x => x.Posts)’, Bascially I have a topic that has many posts under it - I was under the impression that if I added the following at the top of my topic map
Cache.ReadWrite().IncludeAll();
its caches map and hasManys until either the underlying data changes of app is restarted? I have my L2 cache configured like so
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("MyAppConString")))
.Cache(c => c.ProviderClass<SysCacheProvider>().UseQueryCache())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<MembershipUserMap>())
Etc…etc..
In my Topic map I have the following (I have removed a load of the normal maps to shorten it), and you can see the HasMany on the posts.
public TopicMap()
{
Cache.ReadWrite().IncludeAll();
Id(x => x.Id);
Map(x => x.Name);
*lots of other normal maps*
References(x => x.Category).Column("Category_Id");
References(x => x.User).Column("MembershipUser_Id");
References(x => x.LastPost).Column("Post_Id").Nullable();
HasMany(x => x.Posts)
.Cascade.AllDeleteOrphan().KeyColumn("Topic_Id")
.Inverse();
*And a few other HasManys*
}
So if I grab all topics, loop over and do the following
Topic.Posts.Count()
Using SqlProfiler I see the get all posts for each topic (First hit), but if I reload the page I still see all the gets posts for topic queries??
Am I missing something?? I thought this should be cached now??
Ayende has a good post explaining the same problem that you have run into.
Basically, what you have there is caching only IDs of Posts, not the posts itself. Notice that you have enabled caching for Topic entities, but you probably didn't do the same for Post entities in PostMap.
Add Cache.ReadWrite() to PostMap also.
Notice that NHibernate won't be able to do the collection caching right in most of the cases if you are using eager loading with Fetch.Join(), like you have in your other question.
Here's a very good overview of the caching and the problems you might have with eager loading collections.
I've come across a case, we're I've a column mapped twice (un-beknown to us..), and now updates are throwing the "Parameter +1 doesn't exist error".
Is there any suitable way we can achieve the following mapping?
(Please note, this is an inherited database...)
References(x => x.Matter).Columns(new[] { "c_client", "c_matter" }).NotFound.Ignore();
References(x => x.Client).Column("c_client");
An option for you could be to mark the Client column as read only.
References(x => x.Matter).Columns(new[] { "c_client", "c_matter" }).NotFound.Ignore();
References(x => x.Client).Column("c_client").ReadOnly();
This should make it so NHiberante does not try to update it
This is an invalid mapping. You can't use the same column twice.
My suggestion is that you map c_matter and c_client as scalar properties and use queries to retrieve the corresponding matters and clients.
I need some help.
I'm just starting out with NHibernate and I'm using Fluent for mappings. Everything seemed to work fine until today.
Here is the story:
I have two tables in my db: Store and WorkDay
The first table contains info about the store, and the WorkDay table contains info about the days of week and start/end time when the store is open.
Store contains a Guid StoreID PK column that is referenced in the WorkDay table.
So I have a mapping file for Store where I have a HasMany association with the WorkDay table, and a corresponding POCO for Store.
Now, when I fill in all the necessary data and try to persist it to database, I get an exception telling me that the insert into table WorkDay failed because the StoreID had null value and the table constraint doesn't allow nulls for that column (which is, of course, expected behavior).
I understand the reason for this exception, but I don't know how to solve it.
The reason why the insert fails is because the StoreID gets generated upon insert, but the [b]WorkDay[/b] collection gets saved first, in the time when the StoreID hasn't yet been generated!
So, how do I force NHibernate to generate this ID to pass it to dependent tables? Or is there another solution for this?
Thank you!
Here's the code for StoreMap
public class StoreMap : ClassMap<Store> {
public StoreMap() {
Id(x => x.StoreID)
.GeneratedBy.GuidComb();
Map(x => x.City);
Map(x => x.Description);
Map(x => x.Email);
Map(x => x.Fax);
Map(x => x.ImageData).CustomType("BinaryBlob");
Map(x => x.ImageMimeType);
Map(x => x.Name);
Map(x => x.Phone);
Map(x => x.Street);
Map(x => x.Zip);
HasMany(x => x.WorkDays)
.Inverse().KeyColumn("StoreID").ForeignKeyCascadeOnDelete()
.Cascade.All();
}
}
and this is for the WorkDayMap
public class WorkDayMap : ClassMap<WorkDay>{
public WorkDayMap() {
Id(x => x.WorkDayID)
.GeneratedBy.Identity();
Map(x => x.TimeOpen);
Map(x => x.TimeClose);
References(x => x.Store).Column("StoreID");
References(x => x.Day).Column("DayID");
}
}
NHibernate shouldn't insert the WorkDay first, so there must be an error in your code. Make sure you do all of the following:
Add all WorkDay objects to the WorkDays collection.
Set the Store property on all WorkDay objects to the parent object.
Call session.Save() for the Store but not for the WorkDay objects.
edit: You should also note that ForeignKeyCascadeOnDelete() won't change anything at runtime. This is just an attribute for the hbm2ddl tool. If you want NHibernate to delete removed entries, use Cascade.AllDeleteOrphan().
It probably inserts the Workday before the Store because the first has an identity generator. This forces NH to execute an INSERT statement to generate the ID. guid.comb is generated in memory, NH doesn't need the database.
NH tries to access the db at the latest possible point in time, to avoid unnecessary updates and to make use of batches. Workday is inserted when you call session.Save, Store is inserted when it flushes the session next time (eg. on commit).
You shouldn't use the identity generator (unless you are forced to use it). It is bad for performance anyway.
If it still doesn't work, you probably need to remove the not-null constraint on the foreign key. NH can't resolve it every time. There is some smart algorithm which is also able to cope with recursive references. But there are sometimes cases where it sets a foreign key to null and updates it later even if there would be a solution to avoid it.
I have the following fluent mapping set up for an entity:
*Id(x => x.Id);
References(x => x.UserNominee).UniqueKey("UQ_SurveyNominee");
References(x => x.SurveyRequest).UniqueKey("UQ_SurveyNominee");
Map(x => x.NominationDate).Not.Nullable();*
Unfortunately the unique index is only created on one of the columns on the resulting SQL Server table and not both of them as I expected. What am I doing wrong?
Regards
mjj
OK I have managed to get this to work but I am not sure why it should make any difference. I had to change the mapping on the parent "SurveyRequest" entity. I changed the mapping from:
HasMany(x => x.SurveyAwarenessNominees)
.KeyColumn("SurveyRequest_Id")
.LazyLoad()
.Inverse()
;
to
HasMany(x => x.SurveyAwarenessNominees).Cascade.All().Inverse();
My unique index is now correctly created on the two foreign key columns.