【问题标题】:What's the best practice to return from a function which returns Task<CustomType>, if no data found? [closed]如果找不到数据,从返回 Task<CustomType> 的函数返回的最佳做法是什么? [关闭]
【发布时间】:2021-09-20 03:27:42
【问题描述】:

我有以下代码,我的问题是,当没有找到数据时,返回的最佳做法是什么? 目前我正在返回 null,但有没有更好的建议方法,比如抛出异常或错误?任何指导都会被欣赏。 这是我的 Web api 获取端点的 Repository 方法。

public async Task<CustomerProfileDto> Handle(GetCustomerProfileQuery request, CancellationToken cancellationToken)
    {
        var query = from Customer in _context.Customers
                            .Include("SystemCustomers")
                            .Include("SystemCustomers.SystemCustomerCreditors")
                            .Include("SystemCustomers.SystemCustomerCreditors.Creditor")
                            .Include("SystemCustomers.SystemRole")
                            .Include("SystemCustomers.SystemRole.System")
                    join SystemCustomer in _context.SystemCustomers on Customer.Id equals SystemCustomer.CustomerId
                    join SystemRoles in _context.SystemRoles on SystemCustomer.SystemRoleId equals SystemRoles.Id
                    join Systems in _context.Systems on SystemRoles.SystemId equals Systems.Id
                    join SystemCustomerCreditor in _context.SystemCustomerCreditors on SystemCustomer.Id equals SystemCustomerCreditor.SystemCustomerId
                    join Creditor in _context.Creditors on SystemCustomerCreditor.CreditorId equals Creditor.Id
                    where Customer.Email == request.Email &&
                         Systems.Code == request.SystemCode &&
                         SystemCustomer.SystemRole.Id == _context.SystemCustomers.Where(au => au.CustomerId == SystemCustomer.CustomerId)
                         .OrderByDescending(au => au.SystemRole.RoleType.Priority)
                         .FirstOrDefault().SystemRole.Id &&
                         (request.ExternalReference == null || Customer.ExternalReference == request.ExternalReference)
                    select new
                    {
                        CustomerData = Customer,
                        Creditorconfig = JsonConvert.DeserializeObject<AuthenticationConfiguration>(Creditor.AuthenticationConfigJObjectData)
                    };
        if (query.Any())
        {
            ///TO-DO: Incorporate CancellationToken if  possible 
            var CustomerData = query.FirstOrDefault().CustomerData;
            var result = _mapper.Map<CustomerProfileDto>(CustomerData);
            var clientaddress = query.FirstOrDefault().Creditorconfig.Clientaddresses.ToList();
            if (clientaddress.Any())
            {
                var addressDetails = new IPAdressDetails { Currentaddress = request.address, Clientaddresses = clientaddress };
                result.IsIPWhitelisted = _addressValidator.IsAddressWhitelisted(addressDetails);
            }    
            return result;
        }
        //whats the best alternative of sending null here?
        return null;
    }

【问题讨论】:

  • 返回null 没问题,但你应该缓存query.FirstOrDefault() 并检查它是否为空,而不是调用.Any()
  • 询问最佳实践的问题是基于意见的,因此在这里偏离主题。见meta.stackoverflow.com/q/296542/62576。请改写您的问题。
  • query.Any(), query.FirstOrDefault(), query.FirstOrDefault(): 你确定你不是连续向数据库发出三个请求吗?这不仅效率低下,而且还可能从每个请求中获得不同的结果,从而使 Handle 方法的逻辑(可能不恰当地命名)无效。

标签: c# async-await task webapi clean-architecture


【解决方案1】:

我能给你的唯一“最佳”实践是不对有效的应用程序路径使用异常。

抛出异常只能用于异常情况,不能用于正常的应用程序流程。如果您知道可以到达并且可以处理路径,那么它不再是例外。

关于返回 null,如果只有一个原因没有返回有效的 CustomerProfileDto,即没有满足请求的对象时,这是完全有效的。

如果有更多原因导致无法完成请求(即 API 端点可以返回 NotFound 或 BadRequest 或其他值,具体取决于请求和/或数据源中的值),则OperationResult 模式可能是比较合适。

OperationResult 是一个概念,用于将响应包装在一个对象中,如果操作成功与否,可以询问该对象,如果正确则获取值,或者如果成功则获取错误消息或错误代码不成功。

我在生产中实际使用的模式的实现(您可以对其进行改进以满足您的需求)如下:

public class Result
{
    private const string EmptyErrorsMessage = "errors cannot be empty";

    public bool Success { get; }

    public ResultType Type { get; }

    public List<string> Errors { get; } = new();

    protected Result(bool success, ResultType type, params string[] errors)
    {
        Success = success;
        Type = type;
        Errors.AddRange(errors);
    }

    public static Result Successful() => new(true, ResultType.Successful);

    public static Result Failed(params string[] errors) => new(false, ResultType.Failed, ValidateErrors(errors));

    public static Result NotFound(params string[] errors) => new(false, ResultType.NotFound, ValidateErrors(errors));

    protected static string[] ValidateErrors(string[] errors) =>
        errors switch
        {
            null => throw new ArgumentNullException(nameof(errors)),
            { Length: 0 } => throw new ArgumentException(EmptyErrorsMessage, nameof(errors)),
            _ => errors
        };
}

public class Result<T> : Result
{
    private const string NoValueOnFailedOperationMessage = "There is no value on a failed operation";

    private readonly T _value;

    public T Value =>
        Success
            ? _value
            : throw new InvalidOperationException(NoValueOnFailedOperationMessage);

    private Result(bool success, ResultType type, T value, params string[] errors) : base(success, type, errors)
    {
        _value = value;
    }

    public static Result<T> Successful(T value) => new(true, ResultType.SuccessfulWithResult, ValidateValue(value));

    public static Result<T> Created(T value) => new(true, ResultType.Created, ValidateValue(value));

    public static new Result<T> Failed(params string[] errors) => new(false, ResultType.Failed, default, ValidateErrors(errors));

    public static new Result<T> NotFound(params string[] errors) => new(false, ResultType.NotFound, default, ValidateErrors(errors));

    private static T ValidateValue(T value) => value ?? throw new ArgumentNullException(nameof(value));
}

你可以让你的存储库方法返回一个Task&lt;Result&lt;CustomerProfileDto&gt;&gt;,如果你有一个实际的返回值,你可以使用这样的返回语句

return Result<CustomerProfileDto>.Successful(foundCustomerProfileDto);

或者如果您找不到客户...

return Result<CustomerProfileDto>.NotFound("Couldn't find the customer"); // Or whatever error message that you want

【讨论】:

    【解决方案2】:

    在第一个位置,我认为您的方法是一个 async 方法,但是您没有在代码中使用任何 await,所以对于第一部分,我认为在您的代码中使用 await 是一个不错的决定代码。 对于您的问题,这是基于您与消费者的合同,有时您可以使用 http 状态代码来处理它,例如,用户请求查看不存在的个人资料,并且不知何故它不适合他自己,所以最好不要给他任何信息只需返回 403。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-05
      • 1970-01-01
      • 2010-09-07
      • 2010-09-26
      • 2010-10-16
      • 2013-03-30
      相关资源
      最近更新 更多