saveuniqueorchardcmsorchardcms-1.8irepository

Orchard 1.8 - Saving record with unique field


I defined in my migration one of the columns of my table as unique. If I try to save a record that violates this uniqueness constraint of course I receive an exception:

A duplicate value cannot be inserted into a unique index.

To save the record I'm using the IRepository Create and Update methods, because it does not have a corresponding part. After receiving the exception above I catch it and then I launch AddModelError to stop the saving procedure. Neverthless Orchard gives me another exception after that:

[AssertionFailure: null id in MyModule.Models.MyRecord entry (don't flush the Session after an exception occurs)]
   NHibernate.Event.Default.DefaultFlushEntityEventListener.CheckId(Object obj, IEntityPersister persister, Object id, EntityMode entityMode) +267
   NHibernate.Event.Default.DefaultFlushEntityEventListener.GetValues(Object entity, EntityEntry entry, EntityMode entityMode, Boolean mightBeDirty, ISessionImplementor session) +95
   NHibernate.Event.Default.DefaultFlushEntityEventListener.OnFlushEntity(FlushEntityEvent event) +139
   NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushEvent event) +448
   NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event) +283
   NHibernate.Event.Default.DefaultAutoFlushEventListener.OnAutoFlush(AutoFlushEvent event) +84
   NHibernate.Impl.SessionImpl.AutoFlushIfRequired(ISet`1 querySpaces) +471
   NHibernate.Impl.SessionImpl.List(IQueryExpression queryExpression, QueryParameters queryParameters, IList results) +477
   NHibernate.Impl.AbstractSessionImpl.List(IQueryExpression queryExpression, QueryParameters parameters) +223
   NHibernate.Impl.ExpressionQueryImpl.List() +189
   NHibernate.Linq.DefaultQueryProvider.ExecuteQuery(NhLinqExpression nhLinqExpression, IQuery query, NhLinqExpression nhQuery) +61
   NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression) +262
   NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression) +26
   Remotion.Linq.QueryableBase`1.GetEnumerator() +83
   System.Linq.Enumerable.ToDictionary(IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer) +173
   System.Linq.Enumerable.ToDictionary(IEnumerable`1 source, Func`2 keySelector) +125
   Orchard.Data.Migration.DataMigrationManager.GetFeaturesThatNeedUpdate() +311
   Orchard.Modules.Data.Migration.<GetNotifications>d__6.MoveNext() +219
   System.Linq.<SelectManyIterator>d__14`2.MoveNext() +507
   System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) +536
   System.Linq.Enumerable.ToList(IEnumerable`1 source) +80
   Orchard.UI.Admin.Notification.NotificationManager.GetNotifications() +151
   Orchard.UI.Admin.Notification.AdminNotificationFilter.OnResultExecuting(ResultExecutingContext filterContext) +380
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +245
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +890
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +890
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +890
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +890
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +97
   System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +241
   System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +29
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +111
   System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +53
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +19
   System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +51
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +111
   Orchard.Mvc.Routes.HttpAsyncHandler.EndProcessRequest(IAsyncResult result) +95
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +606
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +288

If I understand correctly what is causing it, Orchard automatically tries to flush the record into the database even if I add a model error. I tried to change the transaction scope but it didn't work. Is there a way to solve this problem?


Solution

  • You can't use Unique constraint when dealing with content part records. The reason is that those are created early, when no data has been passed to it yet (with all properties being empty/null). This happens inside ContentManager. Hence, all properties on those kinds of records need to be nullable and non-unique.

    But you can use the unique/not-null constraints on classes that are mapped to records you create and use via IRepository directly.