简体   繁体   English

我应该如何从存储库返回自定义结果集?

[英]How should I return custom result sets from repository?

I have business models named Product and Orders like below: 我有名为“ ProductOrders业务模型,如下所示:

public class Product
{
    public int ProductId {get; set;}
    public string Name {get; set;}
}

public class Order
{
    public int OrderId{get; set;}
    public int ProductId {get; set;}
    ...
}

In my repository layer I want to return a collection of products with a count of orders placed against each product, but I can't figure out the correct way or returning custom result sets: 在我的存储库层中,我想返回一个产品集合,其中包含针对每个产品的订单数,但是我无法找出正确的方法或返回自定义结果集:

public ?? GetProductsWithOrderCount()
{
    var resultSet = from p in Products
              join o in Orders on p.ProductId equals o.ProductId into grp
              select new
              {
                   Product = p, 
                   OrdersCount = grp.Count(); // Does not work, just for demonstration
              };
    return resultSet;
}

Now I know I can use IEnumerable<object> as the return type, but I have to cast the result set where I use it in my service layer. 现在,我知道可以使用IEnumerable<object>作为返回类型,但是我必须将结果集强制转换为在服务层中使用它的位置。

Another option is I can create another model and return IEnumerable<ProductWithOrderCount> but this adds unnecessary models that do not represent my system: 另一个选择是我可以创建另一个模型并返回IEnumerable<ProductWithOrderCount>但这会添加不必要的模型,这些模型不代表我的系统:

public class ProductWithOrderCount
{
    public Product Product {get; set;}
    public int OrdersCount {get; set;}
}

Is there any other way to do this? 还有其他方法吗?

This is what DTOs (Data Transfer Objects) are for, and yes, it's perfectly reasonable to add a class to handle the response. 这就是DTO(数据传输对象)的用途,是的,添加一个类来处理响应是完全合理的。 The class you have already, ProductWithOrderCount , is fine, only I would add DTO to the end of the class name to more clearly convey that this is a class intended to house a custom database result. 您已经拥有的类ProductWithOrderCount很好,只有我将DTO添加到类名的末尾以更清楚地表明这是旨在容纳自定义数据库结果的类。

I had a very similar requirement in my app. 我的应用程序中有一个非常相似的要求。 It was for paging since my collection of returned objects would be something like 10, 15 or 20 items but the count would be a full database count of something in the hundreds. 这是用于分页的,因为我返回的对象的集合大约是10、15或20个项目,但是计数将是数百个数据库的完整数据库计数。 It seems like in your case though the number returned and the collection count would be the same. 在您的情况下,虽然返回的数量和收集计数似乎相同。

I ended up creating a generic return type and it looked like this, but you could call the class whatever you want, like EnumerableWithCount 我最终创建了一个通用的返回类型,它看起来像这样,但是您可以根据需要调用该类,例如EnumerableWithCount

public class PagedList<T>
{
    public int TotalCount { get; set; }
    public IEnumerable<T> Items { get; set; }

    public PagedList(IEnumerable<T> collection, int totalCount)
    {
        Items = collection;
        TotalCount = totalCount;
    }
}

If I grasp what you're looking for correctly, you would have a return type that looked something like this. 如果我正确地了解了您要查找的内容,那么您将获得一个看起来像这样的返回类型。

IEnumerable<EnumerableWithCount<Product>>

Your repository layer is the one where all storage abstraction are hidden. 您的存储库层是所有存储抽象都被隐藏的层。 Do not let them leak into your business logic ie. 不要让它们泄漏到您的业务逻辑中。 business code should not be able to issue additional queries to the storage directly, even if it looks very convenient. 业务代码即使看起来非常方便,也不应直接向存储发出其他查询。 Same applies to the exceptions. 同样适用于例外。 If you use some sort of lazy loading you are risking of getting DB level exceptions in the business layer or worse in the presentation. 如果使用某种延迟加载,则有可能在业务层中获得数据库级别的异常,或者在表示中表现得更糟。 So you need to fully load your object tree and handle all DB/connectivity exceptions there or wrap them up into something like StorageException and throw them up. 因此,您需要完全加载对象树并在那里处理所有数据库/连接异常,或者将它们包装到诸如StorageException之类的东西中并抛出它们。

If your business logic needs ProductsWithOrders so be it - you need to create another class ProductsWithOrders. 如果您的业务逻辑需要ProductsWithOrders,那么就可以-您需要创建另一个类ProductsWithOrders。 You can go fancy and create templated class like Ledger which you can later use like new Ledger but I'd personally wait till you have another pair of classes to justify it. 您可以幻想创建像Ledger这样的模板化类,以后可以像新Ledger一样使用,但是我个人想等到您有另一对类来证明它正确。

          from p in Products
          select new ProductWithOrderCount()
          {
               Product = p, 
               OrdersCount = p.Orders.Count(),
          };

Remove the join and use navigation properties. 删除联接并使用导航属性。

The special DTO class is the right way to do it. 特殊的DTO类是正确的方法。

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

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