【问题标题】:Is it bad idea to "share" / resolve (ServiceStack) ServiceB within (ServiceStack) ServiceA?在(ServiceStack)服务中“共享”/解决(ServiceStack)服务是个坏主意吗?
【发布时间】:2015-02-16 17:52:31
【问题描述】:

在我们的代码库中,我看到了不同 ServiceStack 服务之间的一些共享(我认为不合适)。我认为这不是一个好主意,因为每个服务的“边界”变得复杂。以“边界”为例,它可以表示数据库连接的边界,我将以数据库连接边界为例来说明我的意思。

例如,如果我有以下两个服务。

[Route("/service-a/", Verbs = "POST")]
public class DtoServiceA : IReturn<IList<string>>
{
}

public class ServiceA : Service //service stack service
{
    public ServiceA(Funq.Container container) : base(container)
    {
        _container = container; //I didn't type the full code
    }

    public IList<string> Post(DtoServiceA request)
    {
        //do something that belongs to ServiceA
        //then resolve ServiceB and call Post() from ServiceB
        Container.Resolve<ServiceB>().Post(new DtoServiceB());

        return new List<string>();
    }
}

[Route("/service-b/", Verbs = "POST")]
public class DtoServiceB : IReturn<IList<string>>
{
}

public class ServiceB : Service //service stack service
{
    public ServiceB(Funq.Container container) : base(container)
    {
        _container = container; //I didn't type the full code
    }

    public IList<string> Post(DtoServiceB request)
    {
        //do something that belongs to ServiceB
        return new List<string>();
    }
}

假设如果我像这样在 Post 方法中控制数据库连接

    public IList<string> Post(DtoServiceA request)
    {
        //suppose if I control db connection like so
        using (var conn = IDbConnectionFactory.Open())
        {
            //do something that belongs to ServiceA
            //then resolve ServiceB and call Post() from ServiceB
            Container.Resolve<ServiceB>().Post(new DtoServiceB());
            //above line will fail because connection has already been opend by ServiecA.Post()
        }
    }

    public IList<string> Post(DtoServiceB request)
    {
        //suppose if I control db connection like so
        using (var conn = IDbConnectionFactory.Open())
        {

        }
    }

因为数据库连接已经打开,所以显然这不是“共享”服务的好方法。但是我们有一种更复杂的方法来打开数据库连接,基本上它会计算/检测它是否打开,因此不会多次打开连接。但这对我来说是代码味道。

我在其他地方发现人们提出了类似的共享服务方式。我不是 100% 相信这是个好建议。

我可能会做类似下面的事情,并将 using 语句中的代码提取到一个单独的类/类中。

    public IList<string> Post(DtoServiceA request)
    {
        //suppose if I control db connection like so
        using (var conn = IDbConnectionFactory.Open())
        {
            //move code to a searapte "none servicestack service", which
            //can be just a normal c# class
            //so that the "boundary" is being controlled at service stack level
            //and the actual code that does the job is extracted elsewhere
            Resolve<NoneServiceStackServiceA>().DoSomething();
            Resolve<NoneServiceStackServiceB>().DoSomething();
        }
    }

    public IList<string> Post(DtoServiceB request)
    {
        //suppose if I control db connection like so
        using (var conn = IDbConnectionFactory.Open())
        {
            //move code to a searapte "none servicestack service", which
            //can be just a normal c# class
            //so that the "boundary" is being controlled at service stack level
            //and the actual code that does the job is extracted elsewhere
            Resolve<NoneServiceStackServiceB>().DoSomething();
        }
    }

欢迎任何建议/建议。谢谢。

【问题讨论】:

    标签: c# servicestack inversion-of-control


    【解决方案1】:

    从资源使用的角度来看,这不是一个坏主意,因为使用连接池打开额外的数据库连接非常轻量级(SqlServer 的默认设置)。

    如果调用 Service 就像一个“聚合服务”,它只是将多个服务的输出组合成单个服务响应,那很好,Northwind CustomerDetailsService 中的示例如下:

    public class CustomerDetailsService : Service
    {
        public CustomerDetailsResponse Get(CustomerDetails request)
        {
            var customer = Db.SingleById<Customer>(request.Id);
            using (var orders = base.ResolveService<OrdersService>())
            {
                var ordersResponse = orders.Get(new Orders { CustomerId = customer.Id });
                return new CustomerDetailsResponse
                {
                    Customer = customer,
                    CustomerOrders = ordersResponse.Results,
                };
            }
        }
    }
    

    否则从代码架构的角度来看,它并不理想,因为它会模糊依赖关系图,我宁愿拉出common functionality in a shared dependency 或扩展方法,例如:

    ServiceA
      - SharedDep
    
    ServiceB
      - SharedDep
    

    避免服务响应中的接口

    另一方面,我强烈建议discourage the use of Interfaces in Service Responses。集合很少被模拟,IList&lt;T&gt; 尤其无用,因为它实际上总是隐藏一个具体的List&lt;T&gt;,需要将其转换为它才能访问其有用的 LINQ 扩展方法。

    【讨论】:

    • 感谢您的快速响应。你睡几个小时? :_) 我想我在“重用” SSService 时遇到的问题之一是 serviceB 在 serviceA 尝试提交时已经提交/处理了数据库连接。所以我们有一个 ConnectionManager,它本质上是一个工作单元,它计算有多少连接被要求打开,如果 > 0,代码只是忽略 open 的调用,对于 commit/dispose 也是如此。但这似乎是个坏主意。
    • 重新“在服务响应中避免接口”,是的,这完全有道理,我完全同意。但我想我的手指总是只能输入 IList 而不是 List :_)
    • 另外我猜没有“一个架构/模式适合所有场景”。所以我倾向于将 SS 用于设计目的,即处理 HTTP 请求/restful。我尽量不通过强制执行 SS 架构来“污染”核心库。但在我正在处理的代码库中,情况并非如此。 SS 方法基本上是唯一让我有点担心的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-16
    • 1970-01-01
    • 2015-12-25
    • 2015-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多