debugcsharpMinor
Linq-to-Sage: CRUD Operations
Viewed 0 times
sagelinqoperationscrud
Problem
Following-up on my Linq-to-Sage implementation, I ended up implementing all CRUD operations, following the Sage 300 view protocols.
So, the entities derive from classes that look like this:
The
...Which determines how the API is being used for inserting/updating these views. So I added 2 static helper methods in the
The
```
private void WriteEntity(IEnumerable> properties)
{
foreach (var property in properties)
{
try
{
View.Fields.FieldByName(property.FieldName).SetValue(property.Value, false);
}
catch (COMException exception)
{
var error = View.Parent.Parent.Errors[0];
throw new SessionErrorException(error.Message, exception);
}
}
}
private void WriteKeys(T
So, the entities derive from classes that look like this:
namespace SageAPI.Views.PurchaseOrders
{
///
/// Defines view and key field mappings for 'Purchase Orders' view.
///
[MapsTo("PO0620")]
public class PO0620
{
[Key(KeyType.GeneratedByView)]
[MapsTo("PORHSEQ")]
public decimal Key { get; set; }
}
}The
KeyType enum determines how keys are generated:public enum KeyType
{
///
/// Indicates that the key value is specified manually.
///
Manual,
///
/// Indicates that the key value is handled by the view, like an identity column.
///
GeneratedByView,
///
/// Indicates that the key value is handled through composition.
/// Use for a foreign key column referring to the parent view in a composite-key setup.
///
GeneratedByHeader
}...Which determines how the API is being used for inserting/updating these views. So I added 2 static helper methods in the
ViewSet class:private static bool HasAutomaticKey(TEntity entity)
{
return GetKeys(entity).Any(key => key.KeyType == KeyType.GeneratedByView || key.KeyType == KeyType.GeneratedByHeader);
}
private static IEnumerable> GetKeys(TEntity entity)
{
return entity.GetPropertyInfos().Where(property => property.KeyType != null);
}The
WriteKeys and WriteEntity methods are used for writing to the active record:```
private void WriteEntity(IEnumerable> properties)
{
foreach (var property in properties)
{
try
{
View.Fields.FieldByName(property.FieldName).SetValue(property.Value, false);
}
catch (COMException exception)
{
var error = View.Parent.Parent.Errors[0];
throw new SessionErrorException(error.Message, exception);
}
}
}
private void WriteKeys(T
Solution
If you're unsure whether the API is going to throw a
And it's not very DRY.
Instead of repeating this block everywhere (well, you're not repeating it everywhere, but it's not clear whether that's intended):
Consider replacing it with a shorter, Pokemon catch block:
And then have that
That way you'll be throwing a
Since it's a "catch 'em all" handler in a
The dual
ViewException or a COMException, or under which circumstances it's going to throw which, your exception handling has holes.And it's not very DRY.
Instead of repeating this block everywhere (well, you're not repeating it everywhere, but it's not clear whether that's intended):
catch (ViewException exception)
{
var error = View.Parent.Parent.Errors[0];
throw new SessionErrorException(error.Message, exception.InnerException);
}
catch (COMException exception)
{
var error = View.Parent.Parent.Errors[0];
throw new SessionErrorException(error.Message, exception);
}Consider replacing it with a shorter, Pokemon catch block:
catch (Exception exception)
{
OnSessionErrorException(exception);
throw;
}And then have that
OnSessionErrorException method centralize exception handling:private void OnSessionErrorException(Exception exception)
{
var session = View.Parent.Parent;
var sessionError = session.Errors.Count > 0
? session.Errors[0]
: null;
var message = sessionError == null ? exception.Message : sessionError.Message;
if (exception is ViewException)
{
throw new SessionErrorException(message, exception.InnerException);
}
throw new SessionErrorException(message, exception);
}That way you'll be throwing a
SessionErrorException with the correct inner exception and message everytime, with minimal code repetition.Since it's a "catch 'em all" handler in a
ViewSet class, perhaps SessionErrorException would be better off with a less specific ViewSetException type name; the handler could then be renamed OnViewSetException.The dual
try blocks in BeginInsert, make it harder to read than necessary. Just wrap the whole block in a single try block instead:public void BeginInsert(TEntity entity)
{
try
{
if (HasAutomaticKey(entity))
{
View.RecordCreate(ViewRecordCreate.DelayKey);
}
else
{
View.RecordClear();
}
var properties = entity.GetPropertyInfos().Where(property =>
property.ViewName == View.ViewID
&& (property.KeyType == null || property.KeyType == KeyType.Manual)
&& property.EditMode != EditMode.ReadOnly);
WriteEntity(properties);
}
catch (Exception exception)
{
OnViewSetException(exception);
throw;
}
}Code Snippets
catch (ViewException exception)
{
var error = View.Parent.Parent.Errors[0];
throw new SessionErrorException(error.Message, exception.InnerException);
}
catch (COMException exception)
{
var error = View.Parent.Parent.Errors[0];
throw new SessionErrorException(error.Message, exception);
}catch (Exception exception)
{
OnSessionErrorException(exception);
throw;
}private void OnSessionErrorException(Exception exception)
{
var session = View.Parent.Parent;
var sessionError = session.Errors.Count > 0
? session.Errors[0]
: null;
var message = sessionError == null ? exception.Message : sessionError.Message;
if (exception is ViewException)
{
throw new SessionErrorException(message, exception.InnerException);
}
throw new SessionErrorException(message, exception);
}public void BeginInsert(TEntity entity)
{
try
{
if (HasAutomaticKey(entity))
{
View.RecordCreate(ViewRecordCreate.DelayKey);
}
else
{
View.RecordClear();
}
var properties = entity.GetPropertyInfos().Where(property =>
property.ViewName == View.ViewID
&& (property.KeyType == null || property.KeyType == KeyType.Manual)
&& property.EditMode != EditMode.ReadOnly);
WriteEntity(properties);
}
catch (Exception exception)
{
OnViewSetException(exception);
throw;
}
}Context
StackExchange Code Review Q#119675, answer score: 2
Revisions (0)
No revisions yet.