Saving business object in Mono

Saving business object in Mono

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


TygreWolf posted on Monday, March 28, 2011

I have a simple editable child list bound to a DataGridView control.  After making changes to the grid, I use ApplyEdit() and Save methods to save the root business object (thereby saving my child list).  This works in Windows .NET perfectly fine.

On Mono, however, saving the root object provides the following error:

"Type System.Collections.ObjectModel.ObservableCollection`1+Reentrant[Csla.Rules.BrokenRule] is not marked as Serializable."

Below is the Mono stacktrace for the error as well:

"  at System.Runtime.Serialization.Formatters.Binary.BinaryCommon.CheckSerializable (System.Type type, ISurrogateSelector selector, StreamingContext context) [0x0002c] in C:\\cygwin\\tmp\\monobuild\\build\\BUILD\\mono-2.10.1\\mcs\\class\\corlib\\System.Runtime.Serialization.Formatters.Binary\\BinaryCommon.cs:119 \r\n  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.GetObjectData (System.Object obj, System.Runtime.Serialization.Formatters.Binary.TypeMetadata& metadata, System.Object& data) [0x0008e] in C:\\cygwin\\tmp\\monobuild\\build\\BUILD\\mono-2.10.1\\mcs\\class\\corlib\\System.Runtime.Serialization.Formatters.Binary\\ObjectWriter.cs:406 \r\n  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteObject (System.IO.BinaryWriter writer, Int64 id, System.Object obj) [0x00000] in C:\\cygwin\\tmp\\monobuild\\build\\BUILD\\mono-2.10.1\\mcs\\class\\corlib\\System.Runtime.Serialization.Formatters.Binary\\ObjectWriter.cs:316 \r\n  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteObjectInstance (System.IO.BinaryWriter writer, System.Object obj, Boolean isValueObject) [0x00062] in C:\\cygwin\\tmp\\monobuild\\build\\BUILD\\mono-2.10.1\\mcs\\class\\corlib\\System.Runtime.Serialization.Formatters.Binary\\ObjectWriter.cs:303 \r\n  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteQueuedObjects (System.IO.BinaryWriter writer) [0x00005] in C:\\cygwin\\tmp\\monobuild\\build\\BUILD\\mono-2.10.1\\mcs\\class\\corlib\\System.Runtime.Serialization.Formatters.Binary\\ObjectWriter.cs:281 \r\n  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteObjectGraph (System.IO.BinaryWriter writer, System.Object obj, System.Runtime.Remoting.Messaging.Header[] headers) [0x0001f] in C:\\cygwin\\tmp\\monobuild\\build\\BUILD\\mono-2.10.1\\mcs\\class\\corlib\\System.Runtime.Serialization.Formatters.Binary\\ObjectWriter.cs:268 \r\n  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (System.IO.Stream serializationStream, System.Object graph, System.Runtime.Remoting.Messaging.Header[] headers) [0x0005f] in C:\\cygwin\\tmp\\monobuild\\build\\BUILD\\mono-2.10.1\\mcs\\class\\corlib\\System.Runtime.Serialization.Formatters.Binary\\BinaryFormatter.cs:216 \r\n  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (System.IO.Stream serializationStream, System.Object graph) [0x00000] in C:\\cygwin\\tmp\\monobuild\\build\\BUILD\\mono-2.10.1\\mcs\\class\\corlib\\System.Runtime.Serialization.Formatters.Binary\\BinaryFormatter.cs:195 \r\n  at Csla.Serialization.BinaryFormatterWrapper.Serialize (System.IO.Stream serializationStream, System.Object graph) [0x00000] in <filename unknown>:0 \r\n  at Csla.Core.ObjectCloner.Clone (System.Object obj) [0x00000] in <filename unknown>:0 \r\n  at Csla.Core.BusinessBase.GetClone () [0x00000] in <filename unknown>:0 \r\n  at Csla.Core.BusinessBase.System.ICloneable.Clone () [0x00000] in <filename unknown>:0 \r\n  at Csla.DataPortal.Update[BusinessBase`1] (Csla.BusinessBase`1 obj) [0x00000] in <filename unknown>:0 "

Jonny - have you seen anything like this yet?  What I'm doing is pretty basic.  My list is bound directly to the datagridview (instead of using a BindingSource).  Not sure if that has anything to do with it, but it looks like it's a simple serialization issue.

Thoughts?  Thanks!

RockfordLhotka replied on Monday, March 28, 2011

It sounds like mono's implementation of ObservableCollection is wrong - it should be marked as Serializable.

In MonoTouch/MonoDroid we had to implement our own, because they didn't have the type at all. I suppose, worst case, we could reuse our own types in mono too - though a better answer is probably for the mono team to fix this bug.

(assuming I'm correct in my guess here)

TygreWolf replied on Tuesday, March 29, 2011

Hey Rocky -

Also assuming you're correct in your guess, what are the viable options? 1) Fix the mono source file for ObservableCollection and recompile mono; 2) implement ObservableCollection in CSLA; 3) Don't use CSLA.

I really don't like any of those options, being so far into my project on such a tight schedule.  Do you think there are any "quick fixes" so that I might be able to get through this (pretty major) hump?

Thanks for your advice.

-Jason

==UPDATE==

I checked out the latest mono source and looked at the ObservableCollection.cs file.  ObservableCollection is indeed marked as Serializable, however, its private Reentrant class is not.  I'm unsure if this could be causing this issue or not.  Another note - the public ObservableCollection(IEnumerable<T> collection) constructor is not implemented at this time (it throws a new NotImplementedException), though I'm not sure if CSLA uses that constructor for any purpose.

JonnyBee replied on Tuesday, March 29, 2011

Hi,

Have you tried the BindingList based collections?

TygreWolf replied on Tuesday, March 29, 2011

Hey Jonny,

Yes, all of my list objects are inheriting from either BusinessBindingListBase or ReadOnlyBindingListBase.  This error confused me, as well, since I was using the BindingList base classes, and not the base classes that inherit from ObservableCollection.  Again, though - works on Windows .NET but not on Mono.  Typically a mono bug.

RockfordLhotka replied on Tuesday, March 29, 2011

In that case, where is the ObservableCollection coming from in the object graph?

TygreWolf replied on Tuesday, March 29, 2011

I'm not exactly sure.  My best guess - and it's just a guess, as I don't know the CSLA architecture completely - is that somehow a BusinessRulesList (or other list) is getting serialized in the entire process.  BusinessRulesList contains a BrokenRulesCollection, which ultimately inherits from ObservableCollection.

The error I received: "Type System.Collections.ObjectModel.ObservableCollection`1+Reentrant[Csla.Rules.BrokenRule] is not marked as Serializable." seems to indicate that a broken rule could not be deserialized within the collection. 

As I said above, I believe this is an issue with Mono not marking its encapsulated Reentrant class as serializable.  Unfortunately, I'm having uber difficulties in my attempts to get the latest mono code to compile, with all the hoops that one needs to go through, so I can't seem to test if that's the case until I'm able to do that.  It's being more than frustrating, however.

-Jason

ajj3085 replied on Tuesday, March 29, 2011

It might be easier to recompile Csla at this point so that the BRC doesn't inhert OC.  I'm not sure what, if any, effects that would have though.

TygreWolf replied on Tuesday, March 29, 2011

At this point, neither am I.  I was finally able to compile the Mono code with a change to ObservableCollection that added the Serializable attribute to its encapsulated class, but it appeared to have no effect on the outcome of the situation.  That's after assumming I performed the compilation correctly after many painstaking hours of figuring out how to do it.

The BrokenRulesCollection inherits from quite a few things before ending up at ObservableCollection.  At this point, it's still only a guess if that's the issue or not.  I'm still debating whether the effort to change it, while not knowing if it's even the cause, would be worth it in the end - especially with the tight schedule I'm on for this project.

I'm going to attempt to try to find out exactly where the code is breaking in CSLA when executed with Mono, but I'm not sure that's possible, either, as from my experience the MonoDevelop environment doesn't play well with the CSLA when run through its debugger.  MonoTools for VisualStudio is still pretty flaky as well, crashing devenv.exe every so often.

I'd like to get some other opinions on the matter, to see if it's worth trying, if it's something that's going to be addressed in the final release of CSLA 4.2, or if we're just going to chalk it up to a bug in Mono and wait for them to fix their issues (who knows when that will be).

-Jason

RockfordLhotka replied on Tuesday, March 29, 2011

Andy is probably right.

Prior to CSLA 4, BRC was a subclass of (ultimately) BindingList<T>. I am pretty sure we changed it to make it more WPF-friendly, not because CSLA really cares one way or the other.

It might be that the mono build needs a #if to change the base class back to the 3.8 equivalent (probably ReadOnlyBindingList in Csla.Core?)

 

TygreWolf replied on Tuesday, March 29, 2011

Hey Rocky!

You're right about that.  I was just about to post an update to let you all know where I was at.

Testing:
I got nowhere with testing to figure out exactly what the issue was.  MonoDevelop doesn't like projects that build business objects with the CSLA at all.  It blows up when declaring the CSLA managed backing fields for properties - specifically the fact that lambda functions are used with RegisterProperty.  Apparently the compiler throws a fit about it.  Also, MonoTools kept blowing up my VS on me every time I tried to debug with Mono in Visual Studio.

Updates:
I changed the inheritance (as you stated above) so that BRC inherits from ReadOnlyBindingList rather than ReadOnlyObservableCollection, with no issues in compiling and running in WIndows.  After I did this, however, I did receive another Mono error:  System.Windows.Forms.CurrencyManager is not marked as Serializable.  Yep.  Another Mono issue.

I'm going to try once again to fix the necessary classes in Mono, but this is getting extremely frustrating (at no fault of you or CSLA).  I'm just worried for you guys that your effort to port the CSLA to Mono will be fruitless for quite some time until Mono gets all these issues fixed.  As it stands right now, CSLA may be nearly ready for Mono, but Mono is certainly not ready for CSLA.

I'll keep you updated on my efforts.

-Jason

Bowman74 replied on Wednesday, March 30, 2011

Jason,

Can you describe more about the managed backing field issue you encountered?  It sounds similar to what I found in Monodroid.  If it is the same problem I have found a work around that may work for you as well.

Thanks,

Kevin

TygreWolf replied on Thursday, March 31, 2011

Annnnnd, the latest update is that business objects still won't save due to some type of serialization issue.  Here's what I keep getting over and over:

Type System.Windows.Forms.CurrencyManager is not marked as Serializable

  at System.Runtime.Serialization.Formatters.Binary.BinaryCommon.CheckSerializable (System.Type type, ISurrogateSelector selector, StreamingContext context) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.GetObjectData (System.Object obj, System.Runtime.Serialization.Formatters.Binary.TypeMetadata& metadata, System.Object& data) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteObject (System.IO.BinaryWriter writer, Int64 id, System.Object obj) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteObjectInstance (System.IO.BinaryWriter writer, System.Object obj, Boolean isValueObject) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteQueuedObjects (System.IO.BinaryWriter writer) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteObjectGraph (System.IO.BinaryWriter writer, System.Object obj, System.Runtime.Remoting.Messaging.Header[] headers) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (System.IO.Stream serializationStream, System.Object graph, System.Runtime.Remoting.Messaging.Header[] headers) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (System.IO.Stream serializationStream, System.Object graph) [0x00000] in <filename unknown>:0
  at Csla.Serialization.BinaryFormatterWrapper.Serialize (System.IO.Stream serializationStream, System.Object graph) [0x00000] in <filename unknown>:0
  at Csla.Core.ObjectCloner.Clone (System.Object obj) [0x00000] in <filename unknown>:0
  at Csla.Core.BusinessBase.GetClone () [0x00000] in <filename unknown>:0
  at Csla.Core.BusinessBase.System.ICloneable.Clone () [0x00000] in <filename unknown>:0
  at Csla.DataPortal.Update[BusinessBase`1] (Csla.BusinessBase`1 obj) [0x00000] in <filename unknown>:0

If anyone can decode this and let me know what the issue might be, I'll take a look at it and see if I can fix it - as long as it doesn't have to do with editing the Mono Framework.  Compiling that thing is ridiculous.  At this point, is it just easier to give up trying to get CSLA to work on Mono?


And for Kevin - MonoDevelop doesn't like the CSLA Managed Backing Field declarations:

public
 static readonly PropertyInfo<string> ModelNumberProperty = RegisterProperty<string>(c => c.ModelNumber);

It throws a fit at the c => c.ModelNumber with 3 separate errors (I don't have access to the error information at the moment, but I can provide it later if you like).

My real issue here is not coding CSLA business objects with MonoDevelop.  My issue is not being able to save business objects while running in Mono.

-Jason

RockfordLhotka replied on Thursday, March 31, 2011

With that error it sounds like your business object somehow has a reference to a UI object - that directly or indirectly references the CurrencyManager.

This could be through an event handler, or an explicit reference on your part.

But it sounds somewhat like the event handler issues from 2000-2001 when I first created CSLA and had to figure out how to mark event delegates as non-serialized.

TygreWolf replied on Thursday, March 31, 2011

Interestingly, the business object has absolutely no references to the UI, but what you said might now lead me to believe this could be another databinding issue with Mono itself.  My main class contains an editable child list.  This list is bound to a DataGridView control without using a BindingList object:

dgvUsers.DataSource = Profile.ProfileUsers;

The save method seems to work well against changes to the grid when used in Windows.  No errors, and no "First Chance Exceptions".  When the executable is run on Mono, that's where the issue occurs.

After considering this, I just manually filled the grid (rather than use databinding), updated the object through code, and saved the object with no exception.  Once I reloaded, the newly created row on the grid appeared successfully.

I attempted to unbind the DataGridView before save, and rebind afterwards, but so far it still seems to throw the exception.  Looks like it's Mono's implementation of databinding that's creating some type of weird hook to the business object that causes it to blow up.  I'll look into this a little more.

Thanks, Rocky.  At least I have a workaround for now - don't use databinding in Mono.

-Jason

ajj3085 replied on Thursday, March 31, 2011

When the grid gets the ProfileUsers, it might be finding out that the objects implement certain intefaces which expose events, and wiring up to them.  Its probably not unhooking correctly when you null it's datasource.

RockfordLhotka replied on Thursday, March 31, 2011

Though the standard CSLA events are implemented to avoid this issue.

It is possible though, that mono has a bug in the API that CSLA uses to determine whether the event handler is on a serializable object.

Bowman74 replied on Thursday, March 31, 2011

Jason,

Interesting.  I tried same line of code in Monodroid with this for ModelNumber:

public string ModelNumber
{
  get { return GetProperty<string>(ModelNumberProperty); }
  protected set { LoadProperty<string>(ModelNumberProperty, value); }

 

It compiles without difficulty.  The error I encountered was a runtime error with the properties.  It looks like there are some differences with Monodroid.  I completely understand your frustration, the random crashes and bugs in the Monodroid framework drive me to distraction.

Thanks,

Kevin

bastidas replied on Friday, April 01, 2011

What version of mono are you guys using. I'm trying 2.6.7 but I'm getting this error:

Unhandled Exception: System.TypeLoadException: Could not load type 'System.ComponentModel.INotifyPropertyChanged' from assembly 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.

Andres

 

Bowman74 replied on Friday, April 01, 2011

Andreas,

I'm using MonoDroid which uses unique versions of the Mono assemblies.  Probably a completely different set of bugs at play...

Thanks,

Kevin

Copyright (c) Marimer LLC