简体   繁体   English

ASP.NET Web API打破了Repository / Services模式

[英]ASP.NET Web API breaking Repository/Services pattern

I am new to building web APIs and have reached a point where I could use some direction. 我是构建Web API的新手,已经达到了可以使用某个方向的程度。 So lets say for example that I have a Product model and a Attachment model which look something like this: 因此,举个例子说我有一个Product模型和一个Attachment模型,它看起来像这样:

 public class Product
 {
     public Product()
     {
        Attachments = new List<Attachment>();
     }
     public int ID { get; set; }
     public string Name { get; set; }
     public List<Attachment> Attachments { get; set; }
 }

 public class Attachment
 {
     public int ID { get; set; }
     public string Name { get; set; }
 }

Notice that the Product model has a property list of type Attachment . 请注意, Product模型具有Attachment类型的属性列表。 Is is bad practice to utilize the AttachmentRepository to fill this property when I am initializing the Property object in the ProductRepository class? 在初始化ProductRepository类中的Property对象时,利用AttachmentRepository填充此属性是不好的做法吗?

So in the ProductRepository I would do something like this: 所以在ProductRepository我会做这样的事情:

Product product = new Product
{
    ID = SomeId,
    Name = SomeName
    Attachments = AttachmentRepository.GetAttachments(SomeName)
};

Is this poor design and I am approaching this incorrectly? 这是一个糟糕的设计,我接近这个错误? I am not planning to create a Controller for Attachment since there would be no use case for getting an attachment without the context of a Product . 我不打算创建一个Controller for Attachment因为在没有Product的上下文的情况下获取附件没有用例。

"I am not planning to create a Controller for Attachment since there would be no use case for getting an attachment without the context of a Product." “我不打算创建一个附件控制器,因为在没有产品上下文的情况下获取附件没有用例。”

How about the inverse? 反过来怎么样? Would you ever want to get the product without Attachments? 您是否想要获得没有附件的产品?

I find methods that try to do too much not as reusable , because if I am getting a large number of products and am not interested in attachments, I don't want the overhead of also getting attachments for all of those products. 我发现尝试做太多而不是可重用的方法 ,因为如果我获得大量产品并且对附件不感兴趣,我不希望获得所有这些产品的附件的开销。 I then regret the design(whether it was mine or a colleague's), adding a new method just to get Products. 然后我后悔设计(无论是我的还是同事),为了获得产品而添加了一种新方法。

In my controller I would have the call to GetProduct and then a call to GetAttachments. 在我的控制器中,我将调用GetProduct,然后调用GetAttachments。 This would allow you to mix-match these as needed. 这将允许您根据需要混合匹配这些。

Another approach would be to do this in the Products repository, have a method that gets just products(GetProducts), and another GetProductsWithAttachments method that calls GetProducts and also calls GetAttachments. 另一种方法是在Products存储库中执行此操作,使用只获取产品的方法(GetProducts),以及调用GetProducts并调用GetAttachments的另一个GetProductsWithAttachments方法。 Or you could of course have a boolean parameter for , includeAttachments = false) . 或者你当然可以有一个布尔参数, includeAttachments = false) I don't like my DB layer getting that messy. 我不喜欢我的数据库层变得那么混乱。 I let the Controller be the place to pull it all together. 我让控制器成为了把它们拉到一起的地方。

I would also probably not even have Attachments property in Products model, but instead have a ProductResponseModel specific to the controller that is the analogy of a ViewModel. 我甚至可能在Products模型中没有Attachments属性,而是具有特定于控制器的ProductResponseModel,它类似于ViewModel。 It would be the one with both the product information and the Attachments property, and you would populate it from the results of calling the two seperate Repository methods. 它将是包含产品信息和Attachments属性的那个,并且您将从调用两个单独的Repository方法的结果中填充它。

If you do it all at the DB layer, and your Product model from the DB layer has an Attachments property that is only populated when you call GetProductsWithAttachments, then your Product model is half filled . 如果您在数据库层执行所有操作,并且数据库层中的产品模型具有仅在调用GetProductsWithAttachments时填充的附件属性,则您的产品模型将填充一半 Add many other similar properties over time, and you've got a really confusing model because sometimes only some of its properties are populated based on which method of the repository you called. 随着时间的推移添加许多其他类似的属性,并且您有一个非常令人困惑的模型,因为有时只根据您调用的存储库的哪个方法填充其某些属性。 Developers have to start digging into the repo methods to figure out what populates what. 开发人员必须开始深入研究repo方法,以找出填充内容的方法。 If you add Promotions to a Product will you have a GetProductWithAttachments, GetProducts, GetProductWithPromotions, GetProductWithAttachmentsAndPromotions? 如果您将促销添加到产品,您将获得GetProductWithAttachments,GetProducts,GetProductWithPromotions,GetProductWithAttachmentsAndPromotions? You can begin to see how this can get out of hand, and some default parameters would be better if you really want to bake this into your repository. 你可以开始看看这是如何失控的,如果你真的想把它加入你的存储库,一些默认参数会更好。 However: 然而:

Recommended: Composition in the Controller 推荐:Controller中的组合

That's why my preference is to let the controller orchestrate the whole thing. 这就是为什么我的偏好是让控制器协调整个事情。 Call repo methods separately within the controller. 在控制器内单独调用repo方法。 Not sure if your using a REST style or what in your Web API controllers, so just consider this pseudo code: 不确定您是使用REST样式还是Web API控制器中的内容,因此请考虑以下伪代码:

public class ProductController: ApiController
{
  public ProductResponseModel Get(int productId) 
  {
     var model = new ProductResponseModel{
        Product = ProductRepository.Get(productId);
     };
     model.Attachments = AttachmentRepository.GetList(model.Product.Name);

     // I could have flattened out the Product into its properties instead of having a model.Product,
     // but that can be a maintenance problem and requires something like AutoMapper to manage well
     return model;
  }

}

public class ProductResponseModel {
  public Product Product {get;set;}
  public IEnumerable<Attachment> Attachments {get;set;}
}

The ProductResponseModel is an example of composition. ProductResponseModel是一个组合示例。 It creates loose coupling with the DB layer, so you are free to mix and match, ie compose models of what data you need for each API controller. 它创建了与DB层的松散耦合,因此您可以自由地混合和匹配,即组合每个API控制器所需的数据模型。 The ProductResponseModel itself is not very reusable, maybe only within that controller. ProductResponseModel本身不是非常可重用的,可能只在该控制器内。 Another controller that needs a different combination of Product data will have its own SomethingResponseModel and call seperate Repository methods to populate it. 另一个需要不同产品数据组合的控制器将拥有自己的SomethingResponseModel并调用单独的Repository方法来填充它。 Even though we don't get to reuse our *ResponseModels, that's not a big loss because they are simple POCOs. 即使我们没有重用我们的* ResponseModels,但这并不是一个很大的损失,因为它们是简单的POCO。 Getting reuse out of our repo methods is more important, and saving them from becoming more complicated than they need to be. 从我们的repo方法中重用是更重要的,并且避免它们变得比它们需要的更复杂。 Additionally we still have your simple model such as Product and Attachment that are part of the repository layer, so that everyone calling the repo is speaking the same language and using the same common types. 此外,我们仍然拥有您的简单模型,例如ProductAttachment ,它们是存储库层的一部分,因此调用repo的每个人都使用相同的语言并使用相同的常见类型。 So don't make the mistake of having your Repo layer return a ProductResponseModel. 因此,不要错误地让Repo层返回ProductResponseModel。 It's your controller's job to take the Product and populate the ProductResponseModel . 获取Product并填充ProductResponseModel是您的控制器的工作。

I use the naming convention *ResponseModel because if I have a complex parameter for the action method also, then there is a *RequestModel as well. 我使用命名约定* ResponseModel,因为如果我也有一个复杂的action参数参数,那么也有一个* RequestModel。 Ie Request/Response. 即请求/响应。 And if I have an API method that is more RPC style than REST style, such as maybe ProductController.DiscontinueDistribution its return might be very specialized and so I'd have a ProductDiscontinueDistributionResponseModel. 如果我有一个比REST风格更具RPC样式的API方法,例如ProductController.DiscontinueDistribution,它的返回可能非常专业,所以我有一个ProductDiscontinueDistributionResponseModel。 Still these are all simple POCOs just like ViewModels in MVC. 这些都是简单的POCO,就像MVC中的ViewModel一样。

If your not using EF or another ORM, then your Product object probably shouldn't contain a list of Attachment objects -- that should be a construction of (like @AaronLS said) some form of DTO akin to a viewmodel in MVC. 如果您不使用EF或其他ORM,那么您的Product对象可能不应该包含Attachment对象列表 - 这应该是(如@AaronLS所说)某种形​​式的DTO,类似于MVC中的viewmodel。

Something like: 就像是:

public class DTOProductAttachment
{
    Product Product { get; set; }
    List<Attachment> Attachments { get; set; }

    public DTOProductAttachment(int id, string name)  // <-- Product ID, Product Name
    { 
        Product = ProductRepository.GetProduct(id); // <-- Product ID
        Attachments = AttachmentRepository.GetAttachmentsByProductName(name); // <-- Product Name, or ID or whatever joins your tables
    }; 
}

... And I think the importance here is, your "objects" Product and Attachment should be reflective of your database design. ......我认为这里的重要性是,您的“对象” ProductAttachment应该反映您的数据库设计。 Your repositories should act in a way that is intuitive of your architecture. 您的存储库应该以一种直观的架构方式运行。

For example, the AttachmentsRepository.GetAttachmentsByProductName() method above is completely intuitive if Name (Product name) is a foreign key on your Attachments table (probably should be Product.ID but I'm running with your example above.) 例如,上面的AttachmentsRepository.GetAttachmentsByProductName()方法是完全直观的,如果Name (产品名称)是Attachments表上的外键(可能应该是Product.ID,但我正在运行上面的示例。)

But, these decisions are relative to your design requirements/goals. 但是,这些决定与您的设计要求/目标有关。 For example, if an Attachment object had a one to many relationship with another object, let's say AttachmentChild , then it would be reasonable to assume you might want to select all of the AttachmentChild objects by Product Name or ID. 例如,如果Attachment对象与另一个对象具有一对多的关系,那么让我们说AttachmentChild ,那么假设您可能希望按Product名称或ID选择所有AttachmentChild对象是合理的。

... In this case, adding a method like AttachmentChildRepository.GetAttachmentsByProductName() seems a little fishy. ...在这种情况下,添加像AttachmentChildRepository.GetAttachmentsByProductName()这样的方法似乎有点可疑。

For salience, you might include the Product Name (or ID) on the AttachmentChild object, even though it's not technically necessary. 为了突出显示,您可以在AttachmentChild对象上包含Product名称(或ID),即使它在技术上不是必需的。

I hope this helps. 我希望这有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM