由于数据库类型而避免使用switch语句

时间:2017-09-16 19:44:02

标签: c# sql-server design-patterns nhibernate

我目前正在开发一个带有数据库的项目,其中我有几个只包含类型枚举的表。比如:

OrderStatus:
ID | Description
1  | Open
2  | Shipped
3  | Canceled
...

当我从数据库中获取类型并希望在代码中检查它们时,这通常会导致switch语句,这有点难以维护,尤其是在将新项目添加到表中时。

switch(order.OrderStatus.ID) {
    case 1:
        handleOpenOrder();
        break;
    case 2:
        handleShippedOrder();
        break;
    case 3:
        handleCanceledOrder();
        break;
    default:
        break;
}

通常会使用继承来解决这个问题,但在这种情况下,我必须检查某些时候从数据库返回的值的类型。

我目前从数据库获取orderstatus作为普通的orderstatus实体。有没有办法让ORM(NHibernate)自动将实体转换为更具体的对象?比如OrderStatusOpen类?

1 个答案:

答案 0 :(得分:0)

如果订单处理程序都包含在一个类中并且只在那里使用,那么您可以简单地执行以下操作,并在实现新的处理程序方法时扩展Dictionary<int, Action>

public class Foo
{
    private readonly _handlerMap = new Dictionary<int, Action>
    {
        { 1, () => handleOpenOrder() },
        { 2, () => handleShippedOrder() },
        { 3, () => handleCanceledOrder() }
    }

    public void TheMethodPreviouslyContainingTheSwitch(Order order)
    {
        var action = _handlerMap[order.OrderStatus.ID];
        action.Invoke();
    }
}

如果你在几个类中有switch语句,我可能会为每个处理程序实现一个类并提取一个匹配的接口。如有必要,我可以详细说明。

修改

我想到的基于接口的方法基本相同。这可能仍然不是一切的答案;-) 我是从头开始编写的(可能无法编译):

public interface IOrderHandler
{
    int StatusId { get; }
    void Handle(Order order);
}

public class OpenOrderHandler : IOrderHandler
{
    public int StatusId => 1;

    public void Handle(Order order)
    {
        // ...
    }
}

public class OrderHandlerFactory
{
    // this factory could be injected to all you dependant classes that need
    // handlers to handle orders

    private readonly _handlerMap = new Dictionary<int, Action>
    {
        // defining the IDs here and in the classes' defintions may be a bit 
        // redudant, but... meh ;)
        { 1, () => new OpenOrderHandler() },
        { 2, () => new ShippedOrderHandler() },
        { 3, () => new CancledOrderHandler() }
    }

    public IOrderHandler CreateHandlerByStatus(int orderStatusId)
    {
        // if the creation of a handler is expensive, it may be useful to
        // only create a single handler that matches the orderStatusId
        var action = _handlerMap[orderStatusId];
        var handler = action.Invoke();
        return handler;
    }

    public ICollection<IOrderHandler> CreateHandlers()
    {
        // it may be fine to create all handlers in each call of this method
        // if the creation is inexpensive
        return _handlerMap.Select(kvp => kvp.Value.Invoke()).ToList();
    }
}

public class Foo
{
    private readonly OrderHandlerFactory _orderHandlerFactory;

    public Foo(OrderHandlerFactory orderHandlerFactory)
    {
        _orderHandlerFactory = orderHandlerFactory;
    }

    public void TheMethodPreviouslyContainingTheSwitch(Order order)
    {
        var handler = _orderHandlerFactory.CreateHandlerByStatus(order.OrderStatus.ID);
        handler.Handle(order);
    }

    public void ThisMayBeUsefulIfMultipleHandlersRelateToAcertainStatus(Order order)
    {
        var handlers = _orderHandlerFactory.CreateHandlers();
        handlers
            .Where(handler => handler.StatusId == order.OrderStatus.ID)
            .All(handler => handler.Handle(order));
    }
}