可查询的正确方法是从服务中获取元素的吗?

时间:2019-07-09 09:14:57

标签: .net asp.net-core

我创建了一个必须从数据库中获取元素的服务。在开发生命周期中,该服务不断发展,现在我有很多方法可以按ID,类别,名称等获取元素。

现在,我试图不返回IEnumerable,而是Iqueryable,并在方法中构建Where子句,即调用服务,但我认为这不是正确的方法。当然,如果在哪里重复了Where子句,我将尝试在服务中移动重复的代码,并使用之前创建的Iqueryable。

OLD:

import Measure from '../src/testing'

describe('Measure Class', () => {
  const measure = new Measure(1, 'm')

  // Number() - works!
  test('Number', () => {
    const number = Number(measure)
    expect(number).toBe(1)
  })

  // String - works!
  test('String', () => {
    const string = String(measure)
    expect(string).toBe('1m')
  })

  /**
   * Number context - fails!
   * Operator '+' cannot be applied to types '1' and 'Measure'.ts(2365)
   */
  test('Number + Measure', () => {
    const number = 1 + measure
    expect(number).toBe(2)
  })

  /**
   * String context - fails!
   * Expected: "is: 1m"
   * Received: "is: 1"
   */
  test('String + Measure', () => {
    const string = 'is: ' + measure
    expect(string).toBe('is: 1m')
  })
})

现在:

public async Task<IEnumerable<Element>> GetElementsByIds(List<int> elementsIds)
        {
            var elements = await _context.Elements
                .Include(e => e.ElementCategories)
                .Include(e=>e.ElementSth)
                .Where(e => elementsIds.Contains(e.Id))
                .ToListAsync();

            if (!elements.Any())
            {
                throw new NotFoundException(nameof(Element), elementsIds);
            }

            return elements;
        }

        public async Task<IEnumerable<Element>> GetElementsPerCategory(string categoryName)
        {
            var elements = await _context.Elements
                .Include(e => e.ElementCategories)
                .Include(e=>e.ElementSth)
                .Where(c=>c.Category.Name == categoryName)
                .ToListAsync();

            if (!elements.Any())
            {
                throw new NotFoundException(nameof(Element), elementsIds);
            }

            return elements;
        }

我想重构服务并以更好的方式创建将来的服务。您对这个问题有经验吗?

1 个答案:

答案 0 :(得分:1)

直接公开IQueryable<>是非常不同的事情,因为它基本上允许对数据库执行任何操作。根据实际查询的内容,甚至您的.Include()调用也可能会被忽略。

在内部服务中公开可查询的内容是编写非常有效的查询的好方法(因为这样便可以得到他们实际需要的内容)。对于不太内部使用或面向公众的API,我真的建议不要直接公开可查询对象以保留控件。

话虽如此,如果您只是想避免代码重复,可以引入一个辅助方法来执行实际的查询,并在方法中传递不同的过滤器:

public async Task<IEnumerable<Element>> GetElementsByIds(List<int> elementsIds)
{
    return await GetElementsInternal(_context.Elements.Where(e => elementsIds.Contains(e.Id)));
}

public async Task<IEnumerable<Element>> GetElementsPerCategory(string categoryName)
{
    return await GetElementsInternal(_context.Elements.Where(e => e.Category.Name == categoryName));
}

private async Task<IEnumerable<Element>> GetElementsInternal(IQueryable<Element> queryable)
{
    var elements = queryable
        .Include(e => e.ElementCategories)
        .Include(e => e.ElementSth)
        .ToListAsync();

    if (!elements.Any())
    {
        throw new NotFoundException(nameof(Element), elementsIds);
    }

    return elements;
}