Child List throws null error in AddNewCore when parent has a property that is a LINQ query of the List

Child List throws null error in AddNewCore when parent has a property that is a LINQ query of the List

Old forum URL: forums.lhotka.net/forums/t/7755.aspx


snakebyteme posted on Friday, October 09, 2009

Is this a bug in CSLA? I need a facade property in the parent that is the result of a linq query of a child list. Everything works fine until I try to add a new item to the child list. When I remove the facade property from the parent, the child list allows new items to be added.

Property in parent :

Public ReadOnly Property FullName() As String
Get
Return Me.SubscriberMemberList.Employee.FullName
End Get
End Property


Property in Child List:

Public ReadOnly Property Employee() As SubscriberMember
Get
Dim employeeMember = (From subscriberMember In Me _
Where subscriberMember.Relationship.ListID = Constants.Relationship.Employee).FirstOrDefault
Return employeeMember
End Get
End Property


AddNewCore in Child List:

Protected Overrides Function AddNewCore() As Object
Dim newObject As SubscriberMember = SubscriberMember.CreateChild()
Me.Add(newObject) 'error occurs here
Return newObject
End Function


The stack trace:

"Object reference not set to an instance of an object."

at lambda_method(ExecutionScope , SubscriberMember )
at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
at Csla.BusinessListBase`2.d__0.MoveNext()
at System.Linq.Enumerable.Contains[TSource](IEnumerable`1 source, TSource value, IEqualityComparer`1 comparer)
at System.Linq.Enumerable.Contains[TSource](IEnumerable`1 source, TSource value)
at Csla.LinqBindingList`1.ItemShouldBeInList(T item)
at Csla.LinqBindingList`1.SourceChanged(Object sender, ListChangedEventArgs e)
at System.ComponentModel.ListChangedEventHandler.Invoke(Object sender, ListChangedEventArgs e)
at System.ComponentModel.BindingList`1.OnListChanged(ListChangedEventArgs e)
at System.ComponentModel.BindingList`1.FireListChanged(ListChangedType type, Int32 index)
at System.ComponentModel.BindingList`1.InsertItem(Int32 index, T item)
at Csla.Core.ExtendedBindingList`1.InsertItem(Int32 index, T item)
at Csla.BusinessListBase`2.InsertItem(Int32 index, C item)
at System.Collections.ObjectModel.Collection`1.Add(T item)
at DeltaDental.BLL.SubscriberMemberList.AddNewCore() in C:\DDSOURCE\DeltaDentalLibrary\trunk\BusinessLayer\Lists\SubscriberMemberList.vb:line 22
at System.ComponentModel.BindingList`1.System.ComponentModel.IBindingList.AddNew()
at Csla.SortedBindingList`1.AddNew()
at System.Windows.Forms.BindingSource.AddNew()
at System.Windows.Forms.CurrencyManager.AddNew()
at System.Windows.Forms.DataGridView.DataGridViewDataConnection.AddNew()
at System.Windows.Forms.DataGridView.DataGridViewDataConnection.OnNewRowNeeded()
at System.Windows.Forms.DataGridView.OnRowEnter(DataGridViewCell& dataGridViewCell, Int32 columnIndex, Int32 rowIndex, Boolean canCreateNewRow, Boolean validationFailureOccurred)
at System.Windows.Forms.DataGridView.SetCurrentCellAddressCore(Int32 columnIndex, Int32 rowIndex, Boolean setAnchorCellAddress, Boolean validateCurrentCell, Boolean throughMouseClick)
at System.Windows.Forms.DataGridView.OnCellMouseDown(HitTestInfo hti, Boolean isShiftDown, Boolean isControlDown)
at System.Windows.Forms.DataGridView.OnCellMouseDown(DataGridViewCellMouseEventArgs e)
at System.Windows.Forms.DataGridView.OnMouseDown(MouseEventArgs e)
at System.Windows.Forms.Control.WmMouseDown(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.DataGridView.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(ApplicationContext context)
at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()
at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()
at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)
at EnrollmentTestHarness.My.MyApplication.Main(String[] Args) in 17d14f5c-a337-4978-8281-53493378c1071.vb:line 81
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

snakebyteme replied on Friday, October 09, 2009

It definitely appears to be LINQ related.

When I change the property in the Child List to a For Each loop, the error does not occur:

For Each Item As SubscriberMember In Me
If Item.Relationship.ListID = Constants.Relationship.Employee Then
Return Item
End If
Next

RockfordLhotka replied on Friday, October 09, 2009

It looks like you may have quite the combination going here.

So you are exposing a list generated from a LINQ query, so that's probably a LinqBindingList. Then you are wrapping it with a SortedBindingList.

The result is that the UI is interacting with the SBL, which is a view over an LBL, which is a view over the original collection.

I must say that this is not something I anticipated. The LinqBindingList is similar to SBL and FBL, but was envisioned as a replacement, not necessarily something to use in combination.

My guess is that the newly added item doesn't meet the condition of the where clause that generated the LBL, and so the newly added item won't be part of the LBL view. But the SBL is confused, because when something is added to SBL it expects the new item to actually be there.

To dig deeper - when you add an item to an SBL, it adds it to the underlying list. In this case the underlying list is your LBL. When you add an item to an LBL, it gets added to the underlying list. In this case that's the real list. BUT, LBL always honors the query (the where clause, etc), so if you add a new item that doesn't meet the query, it'll be in the underlying list, but not in the LBL view.

I suspect this is confusing the SBL, because it thinks it just added an item (which it did) and now the item is instantly not there (because it doesn't meet the LBL query condition).

While this may be a "bug", it isn't fixable, because it is occurring due to the way these different list types are designed.

snakebyteme replied on Monday, October 12, 2009

OK, that makes sense. Can we approach the problem form another angle?
I have a collection of objects that I need to be able to sort and I need a property that returns one (or more) of the objects from the sortable collection of objects which I will use later to perform operations on its/their children.
Do you have a suggested/intended approach to achieve this in CSLA?

Thank you for your reply.

Copyright (c) Marimer LLC