I'm evaluating CodeSmith, and I've set up a test project in C# using the SQL AdventureWorks database with generated CSLA business objects.
It looks good, except for one problem. I get an exception thrown due to the existence of this in the generated code:
private readonly Guid _guidID = Guid.NewGuid();
My approach, and correct me if you think this is a less than ideal approach, is to write a public static method on the List object (SalesOrderDetailList in my example) that in turn calls the generated internal static GetBySalesOrderID method, which in turn uses its generated code to call DataPortal.FetchChild.
Here is the exception:
System.ArgumentException was unhandled
Message=Expression must be writeable
Parameter name: left
Source=System.Core
ParamName=left
StackTrace:
at System.Linq.Expressions.Expression.RequiresCanWrite(Expression expression, String paramName)
at System.Linq.Expressions.Expression.Assign(Expression left, Expression right)
at Csla.Reflection.DynamicMethodHandlerFactory.CreateFieldSetter(FieldInfo field) in C:\Projekt\CslaSource\Csla\Reflection\DynamicMethodHandlerFactory.cs:line 189
at Csla.Reflection.DynamicMemberHandle..ctor(FieldInfo info) in C:\Projekt\CslaSource\Csla\Reflection\DynamicMemberHandle.cs:line 44
at Csla.Core.UndoableHandler.BuildHandlers(Type type) in C:\Projekt\CslaSource\Csla\Core\UndoableHandler.cs:line 55
at Csla.Core.UndoableHandler.GetCachedFieldHandlers(Type type) in C:\Projekt\CslaSource\Csla\Core\UndoableHandler.cs:line 24
at Csla.Core.UndoableBase.CopyState(Int32 parentEditLevel) in C:\Projekt\CslaSource\Csla\Core\UndoableBase.cs:line 130
at Csla.Core.BusinessBase.BeginEdit() in C:\Projekt\CslaSource\Csla\Core\BusinessBase.cs:line 770
at Csla.Core.BusinessBase.System.ComponentModel.IEditableObject.BeginEdit() in C:\Projekt\CslaSource\Csla\Core\BusinessBase.cs:line 695
at System.Windows.Forms.CurrencyManager.OnCurrentChanged(EventArgs e)
at System.Windows.Forms.CurrencyManager.ChangeRecordState(Int32 newPosition, Boolean validating, Boolean endCurrentEdit, Boolean firePositionChange, Boolean pullData)
at System.Windows.Forms.CurrencyManager.List_ListChanged(Object sender, ListChangedEventArgs e)
at System.Windows.Forms.BindingSource.OnListChanged(ListChangedEventArgs e)
at System.Windows.Forms.BindingSource.ResetBindings(Boolean metadataChanged)
at System.Windows.Forms.BindingSource.SetList(IList list, Boolean metaDataChanged, Boolean applySortAndFilter)
at System.Windows.Forms.BindingSource.ResetList()
at System.Windows.Forms.BindingSource.set_DataSource(Object value)
at AdventureWorksWinFormTest.Form1.btnLoad_Click(Object sender, EventArgs e) in C:\Users\Mark.DIGRAD-MARK8710\Documents\CodeSmith Generator\Templates\CSLA\AdventureWorks3\AdventureWorksWinFormTest\Form1.cs:line 30
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.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(IntPtr 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(Form mainForm)
at AdventureWorksWinFormTest.Program.Main() in C:\Users\Mark.DIGRAD-MARK8710\Documents\CodeSmith Generator\Templates\CSLA\AdventureWorks3\AdventureWorksWinFormTest\Program.cs:line 18
at System.AppDomain._nExecuteAssembly(RuntimeAssembly 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, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
Is there any graceful way of dealing with this problem aside from turning off the undo feature? (Which I would consider anyway.)
Mark Sulkowski
If you mark the field as readonly, you also need to mark it as NonSerialized and NotUndoable. It is readonly after all, so the serializers and undo features can't change it after the fact.
This would also mean that you assume responsibility for resetting that value (or avoiding its use) after any serialization or undo operation, because the value becomes meaningless from that point forward.
Hi,
I'd just like to add that a field marked as readonly can only be set in the constructor or at declaration as you do. This causes problems for deserializers and undo.
In CodeSmith I changed the CompositeKeyMethods.cst script. Add the [NonSerialized] and [NotUndoable] attributte to the _guidID parameter.
Copyright (c) Marimer LLC