首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Linq-to-Sage: CRUD操作

Linq-to-Sage: CRUD操作
EN

Code Review用户
提问于 2016-02-11 19:36:04
回答 1查看 142关注 0票数 3

继我的林克对圣人实现之后,我在Sage 300视图协议之后实现了所有CRUD操作。

因此,实体派生自如下类:

代码语言:javascript
复制
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枚举确定如何生成键:

代码语言:javascript
复制
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>类中添加了两个静态助手方法:

代码语言:javascript
复制
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);
}

WriteKeysWriteEntity方法用于写入活动记录:

代码语言:javascript
复制
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的参数:

代码语言:javascript
复制
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
}

然后,我准备实现实际操作:

插入

代码语言:javascript
复制
/// <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);
    }
}

更新

代码语言:javascript
复制
/// <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);
    }
}

删除

代码语言:javascript
复制
/// <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);
    }
}

我不得不将InsertUpdate方法拆分为Begin[Insert|Update]Finalize[Insert|Update],因为根据API文档,为了插入到复合头/详细视图中,我必须:

  1. 初始化字段的RecordClear头。
  2. 设置标题中的字段。
  3. 初始化字段的RecordClear详细信息。
  4. 将字段设置为详细信息。
  5. 插入详细信息。
  6. 如果有更多细节,请转到步骤3。
  7. 插入标头。(这将公布详情)

因此,我的客户端插入代码如下所示:

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积木上做得太过火了。还有什么可以清理的?

EN

回答 1

Code Review用户

回答已采纳

发布于 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块来替换它:

代码语言:javascript
复制
catch (Exception exception)
{
    OnSessionErrorException(exception);
    throw;
}

然后让OnSessionErrorException方法集中处理异常:

代码语言:javascript
复制
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块中即可:

代码语言:javascript
复制
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;
    }
}
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/119675

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档