2
votes

I have a tiny problem with NHibernate and I can't figure out why. Every time I debug or profile the application and I accidently violate a unique constraint NHibernate just won't run any more multiqueries (I'll leave the exception stack trace till last). The flow is this. Everything works fine and then:

Violation of UNIQUE KEY constraint 'UQ__workday__572F4CF4753864A1'. Cannot insert duplicate key in object 'dbo.workday'. The statement has been terminated.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Data.SqlClient.SqlException: Violation of UNIQUE KEY constraint 'UQ__workday__572F4CF4753864A1'. Cannot insert duplicate key in object 'dbo.workday'. The statement has been terminated.terminated.

If I now try to run the same code that executes the query:

var query1 = QueryOver.Of<Invoice>()
    .Fetch(x => x.Company).Eager
    .Fetch(x => x.Workdays).Eager
    .Where(x => x.Id == invoiceId);

var query2 = QueryOver.Of<Invoice>()
    .Fetch(x => x.Company).Eager
    .Fetch(x => x.Products).Eager
    .Where(x => x.Id == invoiceId);

var result = Session.CreateMultiCriteria()
    .Add(query1)
    .Add(query2)
    .List();

var invoice = ((IList) result[0])[0] as Invoice;

if (invoice != null) {
    invoice.CalculateTotals();
}

return invoice;

That code generates the following statement

SELECT this_.invoice_id         as invoice1_10_2_,
       this_.invoice_number     as invoice2_10_2_,
       this_.invoice_prefix     as invoice3_10_2_,
       this_.start_date         as start4_10_2_,
       this_.end_date           as end5_10_2_,
       this_.period             as period10_2_,
       this_.km                 as km10_2_,
       this_.km_price           as km8_10_2_,
       this_.hour_price         as hour9_10_2_,
       this_.gst                as gst10_2_,
       this_.customer_id        as customer11_10_2_,
       this_.printed            as printed10_2_,
       this_.created_at         as created13_10_2_,
       this_.created_by         as created14_10_2_,
       this_.updated_at         as updated15_10_2_,
       this_.updated_by         as updated16_10_2_,
       this_.deleted_at         as deleted17_10_2_,
       this_.deleted_by         as deleted18_10_2_,
       this_.company_id         as company19_10_2_,
       workdays2_.invoice_id    as invoice10_4_,
       workdays2_.workday_id    as workday1_4_,
       workdays2_.workday_id    as workday1_15_0_,
       workdays2_.created_at    as created2_15_0_,
       workdays2_.created_by    as created3_15_0_,
       workdays2_.updated_at    as updated4_15_0_,
       workdays2_.updated_by    as updated5_15_0_,
       workdays2_.quantity      as quantity15_0_,
       workdays2_.unit_price    as unit7_15_0_,
       workdays2_.day           as day15_0_,
       workdays2_.description   as descript9_15_0_,
       workdays2_.invoice_id    as invoice10_15_0_,
       company3_.company_id     as company1_12_1_,
       company3_.created_at     as created2_12_1_,
       company3_.created_by     as created3_12_1_,
       company3_.updated_at     as updated4_12_1_,
       company3_.updated_by     as updated5_12_1_,
       company3_.name           as name12_1_,
       company3_.bankgiro_nr    as bankgiro7_12_1_,
       company3_.invoice_prefix as invoice8_12_1_,
       company3_.moms_reg_nr    as moms9_12_1_,
       company3_.plus_giro_nr   as plus10_12_1_,
       company3_.contact_person as contact11_12_1_,
       company3_.email          as email12_1_,
       company3_.mobile         as mobile12_1_,
       company3_.phone          as phone12_1_,
       company3_.fax            as fax12_1_
FROM   [invoice] this_
       left outer join [workday] workdays2_
         on this_.invoice_id = workdays2_.invoice_id
       left outer join [company] company3_
         on this_.company_id = company3_.company_id
WHERE  this_.invoice_id = 351 /* @p0 */
SELECT this_.invoice_id         as invoice1_10_2_,
       this_.invoice_number     as invoice2_10_2_,
       this_.invoice_prefix     as invoice3_10_2_,
       this_.start_date         as start4_10_2_,
       this_.end_date           as end5_10_2_,
       this_.period             as period10_2_,
       this_.km                 as km10_2_,
       this_.km_price           as km8_10_2_,
       this_.hour_price         as hour9_10_2_,
       this_.gst                as gst10_2_,
       this_.customer_id        as customer11_10_2_,
       this_.printed            as printed10_2_,
       this_.created_at         as created13_10_2_,
       this_.created_by         as created14_10_2_,
       this_.updated_at         as updated15_10_2_,
       this_.updated_by         as updated16_10_2_,
       this_.deleted_at         as deleted17_10_2_,
       this_.deleted_by         as deleted18_10_2_,
       this_.company_id         as company19_10_2_,
       products2_.invoice_id    as invoice10_4_,
       products2_.product_id    as product1_4_,
       products2_.product_id    as product1_13_0_,
       products2_.created_at    as created2_13_0_,
       products2_.created_by    as created3_13_0_,
       products2_.updated_at    as updated4_13_0_,
       products2_.updated_by    as updated5_13_0_,
       products2_.quantity      as quantity13_0_,
       products2_.profit_rate   as profit7_13_0_,
       products2_.unit_price    as unit8_13_0_,
       products2_.description   as descript9_13_0_,
       products2_.invoice_id    as invoice10_13_0_,
       company3_.company_id     as company1_12_1_,
       company3_.created_at     as created2_12_1_,
       company3_.created_by     as created3_12_1_,
       company3_.updated_at     as updated4_12_1_,
       company3_.updated_by     as updated5_12_1_,
       company3_.name           as name12_1_,
       company3_.bankgiro_nr    as bankgiro7_12_1_,
       company3_.invoice_prefix as invoice8_12_1_,
       company3_.moms_reg_nr    as moms9_12_1_,
       company3_.plus_giro_nr   as plus10_12_1_,
       company3_.contact_person as contact11_12_1_,
       company3_.email          as email12_1_,
       company3_.mobile         as mobile12_1_,
       company3_.phone          as phone12_1_,
       company3_.fax            as fax12_1_
FROM   [invoice] this_
       left outer join [product] products2_
         on this_.invoice_id = products2_.invoice_id
       left outer join [company] company3_
         on this_.company_id = company3_.company_id
WHERE  this_.invoice_id = 351 /* @p1 */

I get the following really frustrating exception. Actually from now on every query is a timeout and the application needs to be restarted before I can execute anything. Sometimes it doesn't even help to restart it.

Does anyone know what is up with that?

at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning() at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) at System.Data.SqlClient.SqlDataReader.ConsumeMetaData() at System.Data.SqlClient.SqlDataReader.get_MetaData() at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader() at NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd) in d:\CSharp\NH\nhibernate\src\NHibernate\AdoNet\AbstractBatcher.cs:line 247 at NHibernate.Impl.MultiCriteriaImpl.GetResultsFromDatabase(IList results) in d:\CSharp\NH\nhibernate\src\NHibernate\Impl\MultiCriteriaImpl.cs:line 209 at NHibernate.Impl.MultiCriteriaImpl.DoList() in d:\CSharp\NH\nhibernate\src\NHibernate\Impl\MultiCriteriaImpl.cs:line 171 at NHibernate.Impl.MultiCriteriaImpl.ListIgnoreQueryCache() in d:\CSharp\NH\nhibernate\src\NHibernate\Impl\MultiCriteriaImpl.cs:line 143 at NHibernate.Impl.MultiCriteriaImpl.List() in d:\CSharp\NH\nhibernate\src\NHibernate\Impl\MultiCriteriaImpl.cs:line 91 at FakturaLight.Repositories.InvoiceRepository.GetSingle(Int32 invoiceId) in D:\Projekt\faktura_light\src\FakturaLight\Repositories\InvoiceRepository.cs:line 168 at FakturaLight.WebClient.Controllers.InvoiceController.PrepareEditInvoiceModel(EditInvoiceModel oldModel, Int32 id) in D:\Projekt\faktura_light\src\FakturaLight.WebClient\Controllers\InvoiceController.cs:line 116 at FakturaLight.WebClient.Controllers.InvoiceController.Edit(Int32 id) in D:\Projekt\faktura_light\src\FakturaLight.WebClient\Controllers\InvoiceController.cs:line 82 at lambda_method(Closure , ControllerBase , Object[] ) at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters) at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassd.b__a() at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func1 continuation)preContext, Func1 continuation)1.terminated.

1

1 Answers

5
votes

It's by design. Just create another session.

If the Session throws an exception, the transaction must be rolled back and the session discarded.

(please remember that your sessions should be opened late and closed early)