继我的林克对圣人实现之后,我在Sage 300视图协议之后实现了所有CRUD操作。
因此,实体派生自如下类:
namespace SageAPI.Views.PurchaseOrders
{
/// <summary>
/// Defines view and key field mappings for 'Purchase Orders' view.
/// </summary>
[MapsTo("PO0620")]
public class PO0620
{
[Key(KeyType.GeneratedByView)]
[MapsTo("PORHSEQ")]
public decimal Key { get; set; }
}
}KeyType枚举确定如何生成键:
public enum KeyType
{
/// <summary>
/// Indicates that the key value is specified manually.
/// </summary>
Manual,
/// <summary>
/// Indicates that the key value is handled by the view, like an identity column.
/// </summary>
GeneratedByView,
/// <summary>
/// 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.
/// </summary>
GeneratedByHeader
}...Which确定如何使用API插入/更新这些视图。因此,我在ViewSet<TEntity>类中添加了两个静态助手方法:
private static bool HasAutomaticKey(TEntity entity)
{
return GetKeys(entity).Any(key => key.KeyType == KeyType.GeneratedByView || key.KeyType == KeyType.GeneratedByHeader);
}
private static IEnumerable<EntityPropertyInfo<TEntity>> GetKeys(TEntity entity)
{
return entity.GetPropertyInfos().Where(property => property.KeyType != null);
}WriteKeys和WriteEntity方法用于写入活动记录:
private void WriteEntity(IEnumerable<EntityPropertyInfo<TEntity>> 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(TEntity entity)
{
var keys = entity.GetPropertyInfos().Where(property =>
property.ViewName == View.ViewID && property.KeyType != null);
WriteEntity(keys);
}我必须添加一个EditMode属性,因为ReadOnly不够好,因为我需要能够插入以后无法更新的字段。因此,我将这个枚举作为EditModeAttribute的参数:
public enum EditMode
{
/// <summary>
/// Field can be inserted and updated.
/// </summary>
Editable,
/// <summary>
/// Field cannot be inserted or updated.
/// </summary>
ReadOnly,
/// <summary>
/// Field can be inserted, but not updated.
/// </summary>
InsertOnly
}然后,我准备实现实际操作:
/// <summary>
/// Inserts a single new record into a flat view, or of a detail record in a composed header/detail view.
/// </summary>
/// <param name="entity">The entity that contains the key field and values to insert.</param>
public void Insert(TEntity entity)
{
BeginInsert(entity);
FinalizeInsert();
}
/// <summary>
/// Begins the insertion of a header record into a composed header/detail view.
/// </summary>
/// <param name="entity">The entity that contains </param>
public void BeginInsert(TEntity entity)
{
if (HasAutomaticKey(entity))
{
try
{
View.RecordCreate(ViewRecordCreate.DelayKey);
}
catch (COMException exception)
{
var error = View.Parent.Parent.Errors[0];
throw new SessionErrorException(error.Message, exception);
}
}
else
{
try
{
View.RecordClear();
}
catch (COMException exception)
{
var error = View.Parent.Parent.Errors[0];
throw new SessionErrorException(error.Message, exception);
}
}
WriteEntity(entity);
}
/// <summary>
/// Finalizes the insertion of a header and its details, in a composed header/detail view.
/// </summary>
public void FinalizeInsert()
{
try
{
View.Insert();
}
catch (COMException exception)
{
var error = View.Parent.Parent.Errors[0];
throw new SessionErrorException(error.Message, exception);
}
}/// <summary>
/// Updates a single new record into a flat view, or of a detail record in a composed header/detail view.
/// </summary>
/// <param name="entity">The entity that contains the key field and values to update.</param>
public void Update(TEntity entity)
{
BeginUpdate(entity);
FinalizeUpdate();
}
/// <summary>
/// Begins the insertion of a header record into a composed header/detail view.
/// </summary>
/// <param name="entity">The entity that contains the key field and values to update.</param>
public void BeginUpdate(TEntity entity)
{
WriteKeys(entity);
try
{
if (View.Read(false))
{
WriteEntity(entity);
}
else
{
throw new InvalidKeyException();
}
}
catch (COMException exception)
{
var error = View.Parent.Parent.Errors[0];
throw new SessionErrorException(error.Message, exception);
}
}
/// <summary>
/// Finalizes the updating of a header and its details, in a composed header/detail view.
/// </summary>
public void FinalizeUpdate()
{
try
{
View.Update();
}
catch (COMException exception)
{
var error = View.Parent.Parent.Errors[0];
throw new SessionErrorException(error.Message, exception);
}
}/// <summary>
/// Deletes the specified existing entity.
/// If this is a header entity in a header/detail view, details will also be deleted.
/// </summary>
/// <param name="entity">The entity to delete.</param>
public void Delete(TEntity entity)
{
try
{
WriteKeys(entity);
if (View.Read(false))
{
View.Delete();
}
else
{
throw new InvalidKeyException();
}
}
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);
}
}我不得不将Insert和Update方法拆分为Begin[Insert|Update]和Finalize[Insert|Update],因为根据API文档,为了插入到复合头/详细视图中,我必须:
因此,我的客户端插入代码如下所示:
context.PurchaseOrderHeaders.BeginInsert(header);context.PurchaseOrderDetails.Insert(detail1);context.PurchaseOrderDetails.Insert(detail2);context.PurchaseOrderDetails.Insert(detail3);context.PurchaseOrderHeaders.FinalizeInsert();
及删除:
变量标头= context.PurchaseOrderHeaders.SingleOrDefault(po => po.Number ==“987654”;if (报头!=空){context.PurchaseOrderHeaders.Delete(标头);}
如果幸运的话,API引发的异常是COMException,或者ViewException。无论哪种方式,有用的信息都在Session.Errors集合中,我正在从View中访问这个集合--所以我从那里获取消息,从抛出的COMException ( ViewException有一个内部COMException)获得堆栈跟踪,然后抛出我自己的SessionErrorException。
我无法摆脱这样一种感觉,那就是我在try...catch积木上做得太过火了。还有什么可以清理的?
发布于 2016-02-11 20:31:44
如果您不确定API是会抛出ViewException还是COMException,或者在什么情况下会抛出哪个,那么异常处理就会有漏洞。
而且不太干燥。
与其在任何地方重复这个块(嗯,您不是在到处重复它,但不清楚这是否有意):
捕获(ViewException异常){ var错误= View.Parent.Parent.Errors0;抛出新的SessionErrorException(error.Message,exception.InnerException);} catch (COMException异常){ var错误= View.Parent.Parent.Errors0;抛出新SessionErrorException(error.Message,异常);}
考虑用一个更短的Pokemon catch块来替换它:
catch (Exception exception)
{
OnSessionErrorException(exception);
throw;
}然后让OnSessionErrorException方法集中处理异常:
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);
}这样,每次都会抛出一个具有正确的内部异常和消息的SessionErrorException,并且尽量减少代码的重复。
因为它是ViewSet类中的"catch‘all“处理程序,所以SessionErrorException最好使用一个不那么具体的ViewSetException类型名称;然后可以将处理程序重命名为OnViewSetException。
双try块在BeginInsert中,使得读起来比必要时更难。只需将整个块包装在一个try块中即可:
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;
}
}https://codereview.stackexchange.com/questions/119675
复制相似问题