Objective:
Create a parent-child relationship such that modifications to the parent's list of children will propagate to all of the children and have NHibernate do the heavy lifting.
The parent-child relationship will be a Has-Many on a self referencing table.
Problem:
Any attempt at deleting the parent (the root) object causes exceptions instead of the expected behavior of deleting the child objects.
Versions of stuff I am using:
Microsoft SQL Server Management Studio Version 10.0.4064.0
FluentNHibernate Version 1.3
NHibernate Version 3.2.0.4
Below is the set of current class objects and table structure I am using to replicate this behavior.
// Entity
class Task
{
ID { get; set; }
public virtual IList<Task> Children { get; set; }
public virtual byte[] Version { get; protected set; }
public virtual bool IsNew() { return ID <= 0; }
public Task()
{
this.Children = new System.Collections.Generic.List<Task>();
}
// Other properties excluded for brevity
}
// Map
class TaskMap : ClassMap<Task>
{
TaskMap()
{
Table("Task");
Id(x => x.ID, "ID")
.GeneratedBy.HiLo(
"NH_HiLo", "NextHigh", "100",
string.Format("TableName = '{0}'", "Task"));
HasMany<Task>(x => x.Children)
.KeyColumn("ParentTaskID")
.Cascade.AllDeleteOrphan();
// Other properties omitted for brevity
Version(x => x.Version)
.Not.Nullable()
.Generated.Always()
.Column("Version")
.CustomSqlType("timestamp");
}
}
// Repository Delete Method:
public virtual void Delete(Task value)
{
// CurrentSession is an ISession object that is currently open
using (var transaction = CurrentSession.BeginTransaction())
{
try
{
CurrentSession.Delete(value);
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
}
}
// Test Case using NUnit.Framework and FluentNHibernate.Testing:
[TestFixtureSetUp]
public void SetUpFixture()
{
_repository = new Repository();
}
[Test]
public void MappingTest()
{
var task = new Task(); // Omitted assigning other properties for brevity
var entity = new Task(); // Omitted assigning other properties for brevity
entity.Children.Add(task);
_entity = new PersistenceSpecification<Task>(_repository.CurrentSession)
.VerifyTheMappings(entity);
}
[TearDown]
public void TearDown()
{
if (_entity != null && !_entity.IsNew())
{
_repository.Delete(_entity);
_entity = null;
}
}
--Table Script:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Task](
[ID] [bigint] NOT NULL,
[ParentTaskID] [bigint] NULL, -- Notice it DOES HAVE a NULLable FK reference.
[Version] [timestamp] NOT NULL,
CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED(
[ID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Task] WITH CHECK ADD CONSTRAINT [FK_TasksChild_TasksParent]
FOREIGN KEY([ParentTaskID])
REFERENCES [dbo].[Task] ([ID]) -- Notice the self table reference for child objects
GO
ALTER TABLE [dbo].[Task] CHECK CONSTRAINT [FK_TasksChild_TasksParent]
GO
With the above table and classes, changing the cascade to these options and executing the specified during the teardown of the test, these are the results.
With Cascade.AllDeleteOrphan:
Simply calling delete on the parent object I get this exception:
NHibernate.StaleObjectStateException was unhandled by user code
Message=Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Task#1015859]
Source=NHibernate
EntityName=Entities.Task
StackTrace:
at NHibernate.Persister.Entity.AbstractEntityPersister.Check(Int32 rows, Object id, Int32 tableNumber, IExpectation expectation, IDbCommand statement) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 2178
at NHibernate.Persister.Entity.AbstractEntityPersister.Delete(Object id, Object version, Int32 j, Object obj, SqlCommandInfo sql, ISessionImplementor session, Object[] loadedState) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 2912
at NHibernate.Persister.Entity.AbstractEntityPersister.Delete(Object id, Object version, Object obj, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 3095
at NHibernate.Action.EntityDeleteAction.Execute() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Action\EntityDeleteAction.cs:line 70
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 136
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 126
at NHibernate.Engine.ActionQueue.ExecuteActions() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 174
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 249
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultFlushEventListener.cs:line 19
at NHibernate.Impl.SessionImpl.Flush() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 1489
at NHibernate.Transaction.AdoTransaction.Commit() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Transaction\AdoTransaction.cs:line 190
at Repositories.Repository.Delete(Task value) in ..\Repositories\Repository.cs:line 22
at TaskTest.TearDown() in ..\Tests\TaskTest.cs:line 76
After iterating through each of the children, recursively digging through those children's children and attempting to delete each child from the bottom up:
NHibernate.ObjectDeletedException was unhandled by user code
Message=deleted object would be re-saved by cascade (remove deleted object from associations)[Task#1016061]
Source=NHibernate
EntityName=Entities.Task
StackTrace:
at NHibernate.Impl.SessionImpl.ForceFlush(EntityEntry entityEntry) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 914
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultSaveOrUpdateEventListener.cs:line 140
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultSaveOrUpdateEventListener.cs:line 76
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultSaveOrUpdateEventListener.cs:line 53
at NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 2662
at NHibernate.Impl.SessionImpl.SaveOrUpdate(String entityName, Object obj) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 549
at NHibernate.Engine.CascadingAction.SaveUpdateCascadingAction.Cascade(IEventSource session, Object child, String entityName, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\CascadingAction.cs:line 249
at NHibernate.Engine.Cascade.CascadeToOne(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 216
at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 181
at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 148
at NHibernate.Engine.Cascade.CascadeCollectionElements(Object parent, Object child, CollectionType collectionType, CascadeStyle style, IType elemType, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 240
at NHibernate.Engine.Cascade.CascadeCollection(Object parent, Object child, CascadeStyle style, Object anything, CollectionType type) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 201
at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 185
at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 148
at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 126
at NHibernate.Event.Default.AbstractFlushingEventListener.CascadeOnFlush(IEventSource session, IEntityPersister persister, Object key, Object anything) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 207
at NHibernate.Event.Default.AbstractFlushingEventListener.PrepareEntityFlushes(IEventSource session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 197
at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 48
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultFlushEventListener.cs:line 18
at NHibernate.Impl.SessionImpl.Flush() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 1489
at NHibernate.Transaction.AdoTransaction.Commit() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Transaction\AdoTransaction.cs:line 190
at Repositories.Repository.Delete(Task value) in ..\Repositories\Repository.cs:line 22
at Repositories.Repository.Delete(Task value) in ..\Repositories\Repository.cs:line 25
at Repositories.Repository.Delete(Task value) in ..\Repositories\Repository.cs:line 25
at Tests.TaskTest.TearDown() in ..\Tests\TaskTest.cs:line 76
After iterating through the children, clearing each of their sets of children, then saving/deleting the parent I get this exception:
NHibernate.StaleObjectStateException was unhandled by user code
Message=Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Task#1015960]
Source=NHibernate
EntityName=Entities.Task
StackTrace:
at NHibernate.Persister.Entity.AbstractEntityPersister.Check(Int32 rows, Object id, Int32 tableNumber, IExpectation expectation, IDbCommand statement) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 2178
at NHibernate.Persister.Entity.AbstractEntityPersister.Delete(Object id, Object version, Int32 j, Object obj, SqlCommandInfo sql, ISessionImplementor session, Object[] loadedState) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 2912
at NHibernate.Persister.Entity.AbstractEntityPersister.Delete(Object id, Object version, Object obj, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 3095
at NHibernate.Action.EntityDeleteAction.Execute() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Action\EntityDeleteAction.cs:line 70
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 136
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 126
at NHibernate.Engine.ActionQueue.ExecuteActions() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 174
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 249
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultFlushEventListener.cs:line 19
at NHibernate.Impl.SessionImpl.Flush() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 1489
at NHibernate.Transaction.AdoTransaction.Commit() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Transaction\AdoTransaction.cs:line 190
at Repositories.Repository.Delete(Task value) in ..\Repositories\Repository.cs:line 22
at TaskTest.TearDown() in ..\Tests\TaskTest.cs:line 76
With Cascade.All, simply calling delete on the parent object I get this exception:
Same as for Cascade.AllDeleteOrphan
After iterating through each of the children, recursively digging through those children's children and attempting to delete each child from the bottom up:
Same as for Cascade.AllDeleteOrphan
After iterating through the children, clearing each of their sets of children, then saving/deleting the parent I get this exception:
No exception: The parent gets deleted correctly but now I have orphaned objects that I do not want!
I have the looked through many blogs/stackoverflow questions/resource docs and have not really seen a solution to this issue.
Here are a few of the links I have dug through already:
How to delete a referenced object using FluentNHibernate (ye olde "deleted object would be resaved by cascade")
Error in Cascade : deleted object would be re-saved by cascade
Setting up Fluent NHibernate one-to-many with cascading deletes using the automapper
key-many-to-one and key-property association: nhibernate won't DELETE items from set
https://nhibernate.jira.com/browse/NH-1050
(notice I have the null FK)
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
(notice I use timestamp optimistic versioning)
https://forum.hibernate.org/viewtopic.php?t=933496
(comment by ernst_pluess about cascade=all threw a warning flag about using HiLo, but is HiLo generated or assigned? because technically NHibernate generates it then assigns it...)
Exception deleting child records in NHibernate
(makes it seem like I have to manually delete all child objects, which takes away the purpose of having cascade!!)
Many of the posts mention inverting the relationship but setting .inverse and making the child own the relationship is totally not the goal here!
I have no idea what I am missing but hopefully this is something really simple to fix that I am overlooking. Any help will be much appreciated!
You are not missing anything. It is a combination of your mapping: a) child does not have a Parent mapped, b) child is versioned, c) collection is not set as inverse (because it cannot be managed by child without mapped parent) d) and finally, most likely due to a bug.
What happens, is that with versioning, any INSERT or UPDATE statement is followed by SELECT... to get the latest timestamp generated by DB Server. But this does not happen in one case:
Collection parent is inserted
parent version selected from DB
child inserted
child version selected from DB
child updated (no inversion) to reference parent
-- NOTHING - child version IS NOT selected...
Because the child version after the relation update is different then the one just incremented in DB... later the StaleException is thrown.
The best you can do is extend the mapping to have a Parent... and make it inverse
Related
Problem
I'm trying to introduce a strongly typed ID in one of my entities. To do so, I followed the instructions on https://andrewlock.net/using-strongly-typed-entity-ids-to-avoid-primitive-obsession-part-3/
The only difference to the instructions above is, that I'm trying to persist my ID as a string instead of a Guid.
A short summary of the instructions (a full example can be downloaded here: https://drive.google.com/file/d/1_mtYp1c8W6qJoEAY-8NPNjYvsSAif4mz/view?usp=sharing):
Create a struct "OrderId" with a property "Value", which returns the string representation of the ID
Create the class for the Order entity and add a property "ID" of type "OrderId"
Create the DbContext with a DbSet
Create a custom ValueConverter that converts OrderId to string and vice versa
Apply the value converter to the Id column of the Order entity
Add an explicit conversion operator from OrderId to string to the OrderId in order to avoid client-side evaluations
With this setup, it's no problem to add entries to the Orders table. BUT: when I try to read them, the following exception is thrown:
System.InvalidCastException
HResult=0x80004002
Message=Invalid cast from 'System.String' to 'StronglyTypedIds.OrderId'.
Source=System.Private.CoreLib
StackTrace:
at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider) in E:\A\_work\644\s\src\mscorlib\shared\System\Convert.cs:line 309
at Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter`2.Sanitize[T](Object value) in /_/src/EFCore/Storage/ValueConversion/ValueConverter`.cs:line 52
at Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter`2.<>c__DisplayClass3_0`2.<SanitizeConverter>b__0(Object v) in /_/src/EFCore/Storage/ValueConversion/ValueConverter`.cs:line 43
at Microsoft.EntityFrameworkCore.Storage.RelationalTypeMapping.CreateParameter(DbCommand command, String name, Object value, Nullable`1 nullable) in /_/src/EFCore.Relational/Storage/RelationalTypeMapping.cs:line 515
at Microsoft.EntityFrameworkCore.Storage.Internal.TypeMappedRelationalParameter.AddDbParameter(DbCommand command, Object value) in /_/src/EFCore.Relational/Storage/Internal/TypeMappedRelationalParameter.cs:line 66
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalParameterBase.AddDbParameter(DbCommand command, IReadOnlyDictionary`2 parameterValues) in /_/src/EFCore.Relational/Storage/Internal/RelationalParameterBase.cs:line 45
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.CreateCommand(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues) in /_/src/EFCore.Relational/Storage/Internal/RelationalCommand.cs:line 375
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, DbCommandMethod executeMethod, IReadOnlyDictionary`2 parameterValues) in /_/src/EFCore.Relational/Storage/Internal/RelationalCommand.cs:line 149
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteReader(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues) in /_/src/EFCore.Relational/Storage/Internal/RelationalCommand.cs:line 119
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer) in /_/src/EFCore.Relational/Query/Internal/QueryingEnumerable.cs:line 111
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext() in /_/src/EFCore.Relational/Query/Internal/QueryingEnumerable.cs:line 93
at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found) in E:\A\_work\286\s\corefx\src\System.Linq\src\System\Linq\First.cs:line 63
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ResultEnumerable`1.GetEnumerator() in /_/src/EFCore/Query/Internal/LinqOperatorProvider.cs:line 294
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.<_TrackEntities>d__17`2.MoveNext() in /_/src/EFCore/Query/Internal/LinqOperatorProvider.cs:line 185
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext() in /_/src/EFCore/Query/Internal/LinqOperatorProvider.cs:line 143
at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found) in E:\A\_work\286\s\corefx\src\System.Linq\src\System\Linq\First.cs:line 63
at System.Linq.Enumerable.First[TSource](IEnumerable`1 source) in E:\A\_work\286\s\corefx\src\System.Linq\src\System\Linq\First.cs:line 14
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass15_1`1.<CompileQueryCore>b__0(QueryContext qc) in /_/src/EFCore/Query/Internal/QueryCompiler.cs:line 132
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate) in E:\A\_work\286\s\corefx\src\System.Linq.Queryable\src\System\Linq\Queryable.cs:line 741
at StronglyTypedIds.Program.Main() in <path_to_project>\StronglyTypedIds\StronglyTypedIds\Program.cs:line 29
The weird thing is, that it seems to depend on how I build my Where-query:
var order = dbContext.Orders.Where(o => (string)o.Id == "1").ToList(); // works
const string idAsStringConstant = "1";
order = dbContext.Orders.Where(o => (string)o.Id == idAsStringConstant).ToList(); // works
var idAsStringVariable = "1";
order = dbContext.Orders.Where(o => (string)o.Id == idAsStringVariable).ToList(); // doesn't work
order = dbContext.Orders.Where(o => (string)o.Id == OrderId.Parse("1").StringValue).ToList(); // doesn't work
Steps to reproduce
Here is a small VS2019 solution with an example: https://drive.google.com/file/d/1_mtYp1c8W6qJoEAY-8NPNjYvsSAif4mz/view?usp=sharing
Further technical details
EF Core version: 2.2.6
Database Provider: Microsoft.EntityFrameworkCore.SqlServer and Microsoft.EntityFrameworkCore.Sqlite
Operating system: Windows 10 1809
IDE: (e.g. Visual Studio 2019 16.1.6)
Ok, it's a bug in Entity Framework. If I install the preview of version 3.0 of .NET Core and Entity Framework Core it works as expected. Further there seems to be a fix for version 2.2 which just wasn't released yet (see https://github.com/aspnet/EntityFrameworkCore/issues/17610).
What can cause the following error in c# when using NHibernate and Firebird database?
2015-08-17 08:27:04,962 [21] [(null)] ERROR Smartsign.Server.Core.Server Unhandled exception: System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
at System.ThrowHelper.ThrowArgumentOutOfRangeException()
at System.Collections.Generic.List`1.get_Item(Int32 index)
at FirebirdSql.Data.FirebirdClient.FbParameterCollection.get_Item(Int32 index) in c:\Users\Jiri\Documents\devel\NETProvider\working\NETProvider\src\FirebirdSql.Data.FirebirdClient\FirebirdClient\FbParameterCollection.cs:line 58
at FirebirdSql.Data.FirebirdClient.FbParameterCollection.GetParameter(Int32 index) in c:\Users\Jiri\Documents\devel\NETProvider\working\NETProvider\src\FirebirdSql.Data.FirebirdClient\FirebirdClient\FbParameterCollection.cs:line 315
at System.Data.Common.DbParameterCollection.System.Collections.IList.get_Item(Int32 index)
at NHibernate.Type.Int32Type.Set(IDbCommand rs, Object value, Int32 index)
at NHibernate.Type.NullableType.NullSafeSet(IDbCommand cmd, Object value, Int32 index)
at NHibernate.Type.NullableType.NullSafeSet(IDbCommand st, Object value, Int32 index, ISessionImplementor session)
at NHibernate.Persister.Entity.AbstractEntityPersister.Dehydrate(Object id, Object[] fields, Object rowId, Boolean[] includeProperty, Boolean[][] includeColumns, Int32 table, IDbCommand statement, ISessionImplementor session, Int32 index)
at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor session)
at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Object obj, ISessionImplementor session)
at NHibernate.Action.EntityInsertAction.Execute()
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
at NHibernate.Engine.ActionQueue.ExecuteActions()
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
at NHibernate.Impl.SessionImpl.Flush()
at NHibernate.Transaction.AdoTransaction.Commit()
Just for completeness. The exception:
Index was out of range...
with NHibernate WRITE operation mostly means: there is doubled column mapping.
Usually combination of the <property> and <many-to-one>. Also check:
NHibernate mapping index out of range
So I'm trying to use Dapper.net and I'm liking it. What I'm not liking is when I try to batch-insert entities and I get the following error thrown:
Invalid type owner for DynamicMethod.
at System.Reflection.Emit.DynamicMethod.Init(String name,
MethodAttributes attributes, CallingConventions callingConvention,
Type returnType, Type[] signature, Type owner, Module m, Boolean
skipVisibility, Boolean transparentMethod, StackCrawlMark& stackMark)
at System.Reflection.Emit.DynamicMethod..ctor(String name, Type
returnType, Type[] parameterTypes, Type owner, Boolean skipVisibility)
at Dapper.SqlMapper.CreateParamInfoGenerator(Identity identity,
Boolean checkForDuplicates, Boolean removeUnused, IList1 literals) in
D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 3033 at
Dapper.SqlMapper.GetCacheInfo(Identity identity, Object
exampleParameters, Boolean addToCache) in D:\Dev\dapper-dot-net\Dapper
NET40\SqlMapper.cs:line 2138 at
Dapper.SqlMapper.<QueryImpl>d__611.MoveNext() in
D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 1578 at
System.Collections.Generic.List1..ctor(IEnumerable1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) at
Dapper.SqlMapper.Query[T](IDbConnection cnn, String sql, Object param,
IDbTransaction transaction, Boolean buffered, Nullable1
commandTimeout, Nullable1 commandType) in
D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 1479 at
Dapper.SqlMapper.Query(IDbConnection cnn, String sql, Object param,
IDbTransaction transaction, Boolean buffered, Nullable1
commandTimeout, Nullable1 commandType) in
D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 1418 at
NinjaEvaluation.Data.Database.DapperWrapper.<>c__DisplayClass41.b__3(SqlConnection
sqlConnection, SqlTransaction transaction) in
c:\Projects\InHouse\ninjaevaluation\NinjaEvaluation\NinjaEvaluation.Data\Database\DapperWrapper.cs:line
52 at NinjaEvaluation.Data.Database.DapperWrapper.Invoke(Action`2
action) in
c:\Projects\InHouse\ninjaevaluation\NinjaEvaluation\NinjaEvaluation.Data\Database\DapperWrapper.cs:line
68
This happens in a completely normal situation when I run my query like this:
string sql = #" INSERT INTO XXX
(XXXId, AnotherId, ThirdId, Value, Comment)
VALUES
(#XXXId, #AnotherId, #ThirdId, #Value, #Comment)";
var parameters = command
.MyModels
.Select(model => new
{
XXXId= model.XXXId,
AnotherId= model.AnotherId,
ThirdId= model.ThirdId,
Value = model.Value,
Comment = model.Comment
})
.ToArray();
...
sqlConnection.Query(sql, parameters, commandType: commandType, transaction: transaction)
I found the following SO-thread started by someone having the same problem BUT the issue there seems to have been the .NET version (3.5) but I'm running .NET 4.5 and I can't figure out what the problem is.
Any suggestions?
I ran into this error when using an interface instead of the class:
Query<MyObject> worked, while Query<IMyObject> did not
It fails because this scenario using Query[<T>] isn't expecting an array / sequence of parameters. The Execute call-path does expect this, and unrolls the data automatically, executing the SQL once per item - but this isn't the case for Query[<T>], so it tries to create the dynamic method bound to the array (in your case), which isn't allowed. The code should probably detect this much earlier, and just say "nope, that isn't allowed".
You probably want to change your .ToArray() to .Single().
This will be clearer after the next build; the following passes:
public void SO30435185_InvalidTypeOwner()
{
try {
// not shown for brevity: something very similar to your code
Assert.Fail();
} catch(InvalidOperationException ex)
{
ex.Message.IsEqualTo("An enumerable sequence of parameters (arrays, lists, etc) is not allowed in this context");
}
}
I am using EF 4 to retrieve a list of Employees.
public ContentResult AutoCompleteResult(string searchText)
{
List<Employee> list = Employee.GetAllCurrentEmployees();
List<Employee> filteredEmployees = list
.Where(x => x.GetName().ToLower().Contains(searchText.ToLower()))
.ToList();
JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
var jsonString = jsonSerializer.Serialize(filteredEmployees).ToString();
return Content(jsonString);
}
The list is retrieved OK, but when I serialize it, I get this exception;
System.ObjectDisposedException: The ObjectContext instance has been
disposed and can no longer be used for
operations that require a connection.
Generated: Wed, 17 Nov 2010 16:06:56 GMT
System.ObjectDisposedException: The ObjectContext instance has been
disposed and can no longer be used for operations that require a connection.
at
System.Data.Objects.ObjectContext.EnsureConnection()
at
System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption) at
System.Data.Objects.ObjectQuery`1.Execute(MergeOption mergeOption) at
System.Data.Objects.DataClasses.EntityCollection`1.Load(List`1 collection, MergeOption mergeOption) at
System.Data.Objects.DataClasses.EntityCollection`1.Load(MergeOption mergeOption) at
System.Data.Objects.DataClasses.RelatedEnd.Load() at
System.Data.Objects.DataClasses.RelatedEnd.DeferredLoad() at
System.Data.Objects.DataClasses.EntityCollection`1.System.Collections.IEnumerable.GetEnumerator() at
System.Web.Script.Serialization.JavaScriptSerializer.SerializeEnumerable(IEnumerable enumerable, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) at
System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) at
System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat
serializationFormat) at
System.Web.Script.Serialization.JavaScriptSerializer.SerializeCustomObject(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat
serializationFormat) at
System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat
serializationFormat) at
System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object
o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat
serializationFormat) at
System.Web.Script.Serialization.JavaScriptSerializer.SerializeEnumerable(IEnumerable enumerable, StringBuilder sb, Int32 depth, Hashtable objectsInUse,
SerializationFormat
serializationFormat) at
System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat
serializationFormat) at
System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object
o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat
serializationFormat) at
System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object
obj, StringBuilder output, SerializationFormat serializationFormat) at
System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object
obj, SerializationFormat serializationFormat) at
System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object obj) at
SHP.Controllers.EmployeeController.AutoCompleteResult(String searchText) in C:\Documents and Settings\geoffreypayne\My Documents\Visual Studio
2010\Projects\MVC\SHP\SHP\Controllers\EmployeeController.cs:line
623 at lambda_method(Closure , ControllerBase , Object[] ) at
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) at
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext
controllerContext, IDictionary`2 parameters) at
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext
controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) at
System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassd.InvokeActionMethodWithFilters>b__a()
at
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter
filter, ActionExecutingContext preContext, Func`1 continuation)
at
System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassd.<>c__DisplayClassf.<InvokeActionMethodWithFilters>b__c() at
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) at
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext
controllerContext, String actionName)
I find this very odd. I have already retrieved the list of employees and the DataContext has been disposed. So why would I get this error?
It sounds like you have some lazily loaded relationship properties that have not yet loaded (which has an associated "n+1" performance concern). You can try eager loading to see if this helps; otherwise, explicitly load the data for each item in the list, before you close the object-context.
You could turn off lazy loading to resolve this problem.
Inside your 'using' block, try this:
yourObjectContext.ContextOptions.LazyLoadingEnabled = false;
After doing this, I was able to serialize my EF (DbContext-generated) POCO to JSON without any issue.
*Note: Since I've turned off lazy loading... I explicitly pull in related objects I need ahead of time (mostly with .Include() in my query) before the object is serialized to JSON.
Thought I would chime in here with my 2 cents. We had a very big data access layer, and one of the programmers was used to using a using statement for the context like so:
using (EntityModel myContext = EntityConnection)
{
//code in here to grab data
}
We are using the EntityConnection as a static property that serves up a dbContext per current HttpContext. Any method called after his using block would throw the exception 'ObjectContext instance has been disposed' since obviously the context was disposed of after his method call. So if you are only using one entity context per HttpContext, make sure you don't allow the garbage collector to dispose of it by using a using block.
I figured I would throw this in the answers since I know a lot of people use the entity context differently and I see a lot of using blocks in code samples out there.
I prefer to just load up a fat instance declared outside of the using
Customer _custObj;
using (RazorOne rz1 = new RazorOne())
{
_custObj = rz1.Customers.FirstOrDefault(); // .Include = Lazy loading
// Versus Implicit Load
_custObj.AddressReference.Load();
_custObj.Address1Reference.Load();
}
Then I can pass her onto the View or whatever helper really wanted her..
It sounds like some lazy-loading or delayed evaluation that's happening; you can't assume objects are "loaded" until you actually attempt to read from them.
You need to maintain your DataContext until you are completely done handling the objects retrieved from the database to avoid these errors.
I had the same problem and could resolve it by selecting a projection of the object with only the properties required by the caller, instead of returning the complete object.
It seems that when you have many relations in your object, the serializer tries to navigate those.
So, (supposing that your object context is called "Entities") i would try something like this:
using ( Entities context = new Entities() )
{
var employeeProjection = (from e in context.Employee
select new { e.Id, c.FirstName, e.LastName }).ToList();
return employeeProjection;
}
I worth taking a look at your Employee object and make sure you don't have any virtual keywords sticking out there. The virtual keyword is interpreted by Entity Framework as 'lazy-load'. We'd need to see your Employee class to make an absolute assertion why you could be seeing the exception.
I had a variation on this problem. The data context was not closed, but the Object context instance has been disposed error was getting thrown anyway.
It turned out that the object had a self referential foreign key (ie the foreign key referred back into the same table). Accessing the navigation property in when it refers to a null, you get the objectcontext disposed exception instead of, say, a null pointer exception.
eg:
var a = myObject.Name; // works because myObject still has open object context
var b = myObject.SelfReference; // throws objectcontext disposed if SelfReference is null
I found the best way to handle this and keep the using statement you just need to use the include, see sample below:
using (var ctx = new Context(this.connectionString)) {
var query = ctx.[maintable]
.Include(x => x.[theothertable])
.FirstOrDefaultAsync(u => u.UserName.Equals(userName));
}
using (EmployeeContext db= new EmployeeContext())
{
var lst = db.Employees.Select(p=> new {
EmployeeID = p.EmployeeID,
Name = p.Name,
Salary = p.Salary,
Position = p.Position,
Age = p.Age,
Office = p.Office
}).ToList();
return Json(lst, JsonRequestBehavior.AllowGet);
}
I'm using Fluent NHibernate in one of my projects (and ASP.NET MVC application), with LINQ to query to data (using the LINQ to NHibernate libraries).
The objet names are changed to protect the innocent.
Let's say I have the following classes Foo, Bar, Baz, and their appropriate tables in the database (MySQL).
Foo has a many-to-many relationship with both Bar (table "FooBar") and Baz (table "FooBaz"), defined in the Fluent mappings. As such, the class interface is defined as follows:
public class Foo {
public virtual int id { get; set; }
public virtual string name { get; set; }
public virtual string email { get; set; }
public virtual IList<Bar> bars { get; set; }
public virtual IList<Baz> bazes { get; set; }
}
This is a pretty standard class. We can see that a Foo object will have a list of bars and bazes.
The problem comes when trying to do a LINQ query.
If I do a simple query like this, it works fine (the where clause is unimportant) :
var foos = from foo in session.Linq<Foo>()
where email.equals("foo#bar.com")
select foo;
IList<Foo> listFoos = foos.ToList();
This will return a list of Foos, with all the fields populated (id, name, email, bars, bazes). log4net shows that NHibernate performs separate queries for the collections.
The problem arises when I want to load only some fields. For example, I might want to load only the bars in the query, but not the bazes.
This query compiles, but produces an error at runtime:
var foos = from foo in session.Linq<Foo>()
where email.equals("foo#bar.com")
select new Foo()
{
id = foo.id,
name = foo.name,
email = foo.email,
bars = foo.bars
};
IList<Foo> listFoos = foos.ToList();
The error I get is something along the lines of an array index out of bounds exception. The stack trace shows some methods names relating the collection handling on LINQ-NHibernate's side, but nothing else. The query reported by log4net shows no sign of a query on bars, which means than an error was caught before it had the time to perform the query to select the bars.
Did anyone else had this kind of problem before? How did you solve it? I don't want to have to select all the objects everytime I open a web page in my application!
Thank you!
Edit: here is the stacktrace, as requested.
System.IndexOutOfRangeException: L'index se trouve en dehors des limites du tableau. (read: "Index is out of bounds for the array.")
[IndexOutOfRangeException: L'index se trouve en dehors des limites du tableau.]
NHibernate.Transform.TypeSafeConstructorMemberInitResultTransformer.InvokeMemberInitExpression(MemberInitExpression expression, Object[] args, Int32& argumentCount) +404
NHibernate.Transform.TypeSafeConstructorMemberInitResultTransformer.TransformTuple(Object[] tuple, String[] aliases) +150
[QueryException: could not instantiate: Foo]
NHibernate.Transform.TypeSafeConstructorMemberInitResultTransformer.TransformTuple(Object[] tuple, String[] aliases) +265
NHibernate.Loader.Criteria.CriteriaLoader.GetResultColumnOrRow(Object[] row, IResultTransformer resultTransformer, IDataReader rs, ISessionImplementor session) +171
NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies) +330
NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +704
NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +70
NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters) +111
NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor session, QueryParameters queryParameters) +18
NHibernate.Loader.Loader.List(ISessionImplementor session, QueryParameters queryParameters, ISet`1 querySpaces, IType[] resultTypes) +79
NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results) +407
NHibernate.Impl.CriteriaImpl.List(IList results) +41
NHibernate.Impl.CriteriaImpl.List() +35
NHibernate.Linq.<GetEnumerator>d__0.MoveNext() +71
System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) +7665172
System.Linq.Enumerable.ToList(IEnumerable`1 source) +61
FooRepository.List(Int32 count) in C:\...\FooRepository.cs:38
FooController.List() in C:\...\FooController.cs:30
lambda_method(ExecutionScope , ControllerBase , Object[] ) +39
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +17
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +178
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +24
System.Web.Mvc.<>c__DisplayClassa.<InvokeActionMethodWithFilters>b__7() +52
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +254
System.Web.Mvc.<>c__DisplayClassc.<InvokeActionMethodWithFilters>b__9() +19
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +192
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +399
System.Web.Mvc.Controller.ExecuteCore() +126
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +27
System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +7
System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) +151
System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext) +57
System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext) +7
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +181
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75
A couple of suggestions...
First, NHibernate defaults to lazy loading, so there may not even be a need to do the more "efficient" query you're attempting.
Second, the following code does not quite do what your example code does - it creates a list of anonymous types called miniFoos that contain a subset of the fields in Foo. But the net effect is similar to what you seem to be attempting. It also uses what I believe is called LINQ method syntax.
I tested this in on an entity in my own application, which also has an IList property, and it does work (though I should note that I'm currently using Eager loading). I just cut and pasted the working code and substituted your example's names.
using( var session = sessionFactory.OpenSession() )
{
session.BeginTransaction();
var foos =
session.CreateCriteria(typeof(Foo))
.List<Foo>();
var miniFoos =
foos.Select(f => new { f.email, f.bars })
.Where(f => f.email.Equals("foo#bar.com)
.ToList();
session.Close();
}
I tried answering this already, but am not happy with it.
The more I think about your question, the more it seems like you are trying to do your own version of Lazy Loading, which NHibernate handles pretty well on it's own, in my experience.
I know (from a comment) that you switched to Eager Loading because you were not keeping a session open. I think it would be more useful for you in the long run if you focused on that angle.
If I've misunderstood, perhaps you could edit the question to explain why you think you need to handle it yourself?
I could manage to project not the whole collection but its fields
...
Select(a => new Forum {
Id = a.Id,
Name = a.Name,
CategoryName = a.Categories.Select(b => b.Name).First()
})
CategoryName is an computed filed in the Forum Entity
which does not have any mapping with DB
In result I got a small projection of the huge Forum Entity and a possibility to sort on the field.
Hope that it will help somebody.
i think you can solve your problem by defining the result transformer and the result set names, if in some way you can access the Criteria that being generated you can avoid anonymous object.
the problem is that the property names and column names are not the same, and when using projection nhibernate/linq to nhibernate does not check what the column name and the property name. so if u'll define them by yourself in the result properties (i think its called result set) in the criteria before you execute the query it should work.
it's pretty simple, give it a try