Tuesday, September 15, 2009

buffering linq changes for row at a time submits

So it appears linq-to-sql objects do not support anything to cancel changes for an individual row. Here's the parent class I'm using on my business object wrapper between the ui, and my linq entities:
public class BufferedLinqChange:System.ComponentModel.IEditableObject
{
LqGpsDataContext _dataContext;
 
internal BufferedLinqChange(LqGpsDataContext dataContext)
{
_dataContext = dataContext;
}
 
protected void SetBufferedProperty<T>(string key,Action linqAction
,bool linqEqualsValue,Action bufferAction)
{
if (linqEqualsValue)
{
if (Changes.ContainsKey(key))
Changes.Remove(key);
}
else
Changes.InsertOrUpdate(key, linqAction); bufferAction();
}
 
protected Dictionary<String, Action> Changes = new Dictionary<string, Action>();
 
public int ChangeCount { get { return Changes != null ? Changes.Count : 0; } }
public bool hasChanges { get { return Changes != null ? Changes.Count > 0 : false; } }
 
/// <summary>
/// Learned about this from http://www.vbforums.com/showthread.php?t=584096
/// Other sources:
/// Is IEditableObject too hard?
/// http://go.internet.com/?id=474X1146&url=http%3A%2F%2Fwww.madprops.org%2Fblog%2Fis-ieditableobject-too-hard%2F
/// </summary>
#region IEditableObject Members
 
public void BeginEdit()
{
}
 
public void CancelEdit()
{
if (Changes != null)
Changes.Clear();
}
 
public void EndEdit()
{
_dataContext.SubmitChanges();
if (ChangeCount > 0)
{
Changes.ForEach((item) => item.Value.Invoke());
_dataContext.SubmitChanges();
}
}
 
#endregion
}
A sample property in snippet form would be implemented like this:

#region $propertyName$
 
 
$fieldType$_$propertyName$;
public const String STR_$propertyName$ = "$propertyName$";
public $fieldType$ $propertyName$
{
get { return (Changes.ContainsKey(STR_$propertyName$) ? _$propertyName$ : $entityHolder$.$propertyName$); }
set
{
SetBufferedProperty<$fieldType$>(STR_$propertyName$
, () => $entityHolder$.$propertyName$ = value, $entityHolder$.$propertyName$ == value, () => _$propertyName$ = value);
}
}
#endregion

I'll probably go back and make bufferedLinqChange generic so it can hold and require the linq entity type to be defined. But you'll find since the business object now supports IEditableObject, moving to a different record when it is in a bindingSource, automatically starts the beginEdit, and calls endEdit. I overrode that behavior so that a prompt is given to the user to decide to save or not before navigating. The before navigation code is as follows:

private void ConditionalMove(Action OnMoveSuccess)
{
this.Validate();
Action _OnMoveSuccess = () => { OnMoveSuccess();
//    itemChanges = false; 
};
var current = this.bnAssets.BindingSource.Current.DirectCast<ModelAsset>();
if (_epm.hasErrors() || current.hasChanges) //|| itemChanges)
switch ("Unsaved changes exist save?".ToMessageBox("Save changes", MessageBoxButtons.YesNoCancel))
{
case DialogResult.Yes:
this.bnAssets.BindingSource.EndEdit();
var saveSuccess = false;
try
{
this.dc.dc.SubmitChanges();
saveSuccess = true;
}
catch (Exception exception)
{
exception.Message.ToMessageBox();
}
if (saveSuccess) _OnMoveSuccess();
break;
case DialogResult.No:
current.CancelEdit();
this.errorProvider.Clear();
_OnMoveSuccess();
break;
case DialogResult.Cancel:
 
default:
break;
}
else
_OnMoveSuccess();
}

Then a navigation button would point to:

private void bindingNavigatorMoveNextItem_Click(object sender, EventArgs e)
{
ConditionalMove(() => this.bnAssets.BindingSource.MoveNext());
}

No comments:

Post a Comment