C# 我应该将id或实体传递到我的服务中吗

C# 我应该将id或实体传递到我的服务中吗,c#,C#,考虑到我有一个计算客户帐户余额的服务 public interface ICustomerAccountCalculation { Decimal Balance(int customerId); } 像这样传递customer对象比传递customer id更好吗 public interface ICustomerAccountCalculation { Decimal Balance(Customer customer); } 更自然的做法是传递实体,但这通常会导致性能

考虑到我有一个计算客户帐户余额的服务

public interface ICustomerAccountCalculation
{
    Decimal Balance(int customerId);
}
像这样传递customer对象比传递customer id更好吗

public interface ICustomerAccountCalculation
{
    Decimal Balance(Customer customer);
}

更自然的做法是传递实体,但这通常会导致性能问题,因此在大多数实际情况下,只传递键。

只传递将在函数中使用的值。如果customerid足以让您执行进一步的计算,那么只传递那么多-如果需要任何其他字段,则将其作为不同的参数传递给函数


从对象中提取函数是一种很好的做法。函数应该只关注输入与输出。例如,如果您的函数是
float computeBalance(float,float)
,那么它应该能够获取任意两个浮点值并执行计算。传递对象意味着您已读取对象并提取所需字段。。。这不是一件好事:)

我认为这确实是一个你需要回答的问题,因为这要视情况而定。传递实体与仅仅传递id的成本与收益是什么?就您的服务合同而言,id似乎是获取客户账户余额的足够信息(我知道我正在进行假设)。其他一些需要考虑的事情是序列化/反序列化你的实体的成本等等…

但在另一种情况下,这可能有意义,取决于您正在执行的操作。假设该操作需要调用方提供有关客户的更多信息,如果您已经从调用方中加载了该信息,则转到数据库在操作中获取该信息是没有意义的


所以,这要视情况而定

如果函数的目的是计算客户的帐户余额,我将假设客户已经创建并包含帐户余额。我还将假设customerId对于每个客户都是唯一的,因此只需使用customerId即可计算(检索)帐户余额。话虽如此,如果您只需要customerId来创建余额,那么只需传入id就可以了,但是如果您需要customer对象上的其他属性,那么传入整个customer可能是一个更好的主意,或者您可以传入多个参数

例如,如果仅基于customerId创建余额,则可以在balance()方法中执行类似操作(这看起来更像是一次检索,而不是一次计算):


如上面linq查询中所示,如果您只需要customerId,那么继续并只传入该属性,但是如果您需要其他属性,则传入整个customer或传入更多参数。进一步看,您提到这是为了计算余额,这会告诉我您可能需要的不仅仅是customerId,因此在这种情况下,传递更多参数或整个对象可能会更好。

让我们来分解这两种实现的责任

iCustomerAccountId
  • 按ID检索客户
  • 计算
ICCustomerAccountAccountAccount以客户为例
  • 计算

正如您所看到的,第一个版本不仅仅执行计算。因此,这违反了单一责任原则。第二个版本应该是您的第一次尝试,并且只有在明确需要时才更改使用ID的版本。即使如此,如果出于某种原因,您需要按ID进行计算,您始终可以从客户实例中检索它。

今天我考虑了这个问题,并想到了以下解决方案。这只是一个想法,但可能有用

其思想是始终具有实体/对象参数,以统一服务方法。如果调用者没有对象,它将传递一个新的“虚拟”对象,只设置ID。该对象被标记为“未加载”,因此您知道只有id可用。如果服务需要对象中的其他字段,它将检查是否已加载,如果未加载,则将从数据库中加载

示例代码:

// caller with the object already loaded
productService.process(product);
// caller without the object loaded
productService.process(new Product(productId));

// service method
void process(Product product) {

    // Ensure product is loaded, only if we need more fields.
    // We could extract this to a helper method (e.g. ensureLoaded).
    if (!product.isLoaded()) {
        product = database.getProduct(product.getId());
    }

    ...
}
利与弊我明白了:

专业人士

  • 服务方法是统一的(有时不是实体,有时不是id,或者两者都是)
  • 服务方法清楚地使用实体类型,而不仅仅是字符串或长标识符
  • 如果我们以后需要(或不需要)实体中的更多字段,则易于重构。呼叫方代码不必更改
缺点

  • 如果需要更多字段,服务必须调用helper方法
    ensureload

要计算余额,您是要从数据库中获取任何详细信息,还是所有内容都包含在对象中?并非所有内容都已包含在客户中。在这种情况下,无法知道传递实体的性能是否较低。更不用说你只是在传递一个对实体的引用了。嗯,大多数时候int比对象引用的性能更高(稍微高一点)。但是你有一个观点,我想澄清一下,我在考虑web服务,在web服务中,整个对象都将被序列化。我不想激怒你,但我反对你使用单一责任原则作为你的论点。让我们直截了当地说:当SRP更多地关注于类/模块的内聚性,而不是您所使用的参数时,要应用的问题。当然,类可以检索数据,只是执行实际数据检索的类是不同的类(例如DAO类)。这有意义吗?嗯,我是这么想的。即使
icCustomerAccountCalculation
将检索实体的任务委托给另一个服务,为什么它应该知道客户是通过
int
ID检索的?这归结为一个问题
// caller with the object already loaded
productService.process(product);
// caller without the object loaded
productService.process(new Product(productId));

// service method
void process(Product product) {

    // Ensure product is loaded, only if we need more fields.
    // We could extract this to a helper method (e.g. ensureLoaded).
    if (!product.isLoaded()) {
        product = database.getProduct(product.getId());
    }

    ...
}