Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在生产中使用Fluent断言增强默认.NET LINQ不良异常?_C#_.net_Exception_Fluent Assertions - Fatal编程技术网

C# 在生产中使用Fluent断言增强默认.NET LINQ不良异常?

C# 在生产中使用Fluent断言增强默认.NET LINQ不良异常?,c#,.net,exception,fluent-assertions,C#,.net,Exception,Fluent Assertions,当标准的.NET异常对于stacktrace或其他信息(如 序列包含多个匹配元素 我太懒了,每次在Single之前都不会写if-else语句,这就是为什么不久前我开始在代码中使用这种资产(比如FluentAsserts)结构的原因 var singleItem = itemCollection .Where(i => i.Id = id) .ToArray() .ThrowIfEmpty<Item>(searchCriteria: id)) .ThrowIfMo

当标准的.NET异常对于stacktrace或其他信息(如

序列包含多个匹配元素

我太懒了,每次在Single之前都不会写if-else语句,这就是为什么不久前我开始在代码中使用这种资产(比如FluentAsserts)结构的原因

var singleItem = itemCollection
  .Where(i => i.Id = id)
  .ToArray()
  .ThrowIfEmpty<Item>(searchCriteria: id))
  .ThrowIfMoreThanOne<Item>(searchCriteria: id, dumpItems: true))
  .Single();
var singleItem=itemCollection
.其中(i=>i.Id=Id)
.ToArray()
.ThrowIfEmpty(搜索条件:id))
.ThrowIfMoreThanOne(搜索条件:id,dumpItems:true))
.Single();
因此,在执行Single之前,代码会失败,异常更加详细,甚至在异常中包含项。 我不想用它来发明轮子,而是想使用一些现成的生产断言库,这样我就可以编写更可读的代码,比如

var singleItem = itemCollection
  .Where(i => i.Id = id)
  .ToArray()
  .Should().BeNotEmpty().And().HasMoreThanOneElement().For(searchCriteria: id)
  //.Otherwise().Throw<MyCustomException>("maybe with some custom message")
  .Single();
var singleItem=itemCollection
.其中(i=>i.Id=Id)
.ToArray()
.Should().BeNotEmpty()和().hasMorethaneElement().For(搜索条件:id)
//.Others().Throw(“可能带有一些自定义消息”)
.Single();
与FluentAssertions一样,此库是为测试而开发的,而不是为生产而开发的

是否有任何关于生产准备解决方案的建议

可能相关问题:

由于我没有在互联网上找到一些东西,也没有从人们那里得到一些解决方案,所以我在FluentAsserts的启发下发明了我的“轮子”

免责声明:未在生产中进行测试,但进行了一些本地测试和性能测量

背后的想法是使代码/LINQ抛出的异常更加详细

// may throw
// Sequence contains more than one matching element
// or
// Sequence contains no matching element
var single = source.Single(x => x.Value == someEnumValue);
在这种情况下,只有堆栈跟踪可能有助于识别发生时的行,但如果异常经过少数服务(如WCF)层,堆栈跟踪可能会丢失或被覆盖。 通常,您会使异常像这样更加详细 更多

我们可能会看到可以使用相同的异常消息模式,因此(对我来说)显而易见的解决方案是将其包装到一些可重用的通用代码中,并封装这些冗长的内容。所以这个例子看起来像

// throw generic but verbose InvalidOperationException like
// Sequence of type SomeType contains no matching element with the search criteria Value=SomeEnum.Value
var single = source
    .AllowVerboseException()
    .WithSearchParams(someEnumValue)
    .Single(x => x.Value == someEnumValue);
// or CustomException
var single = source
    .AllowVerboseException()
    .WithSearchParams(someEnumValue)
    .IfEmpty().Throws<CustomException>()
    .IfMoreThanOne().Throws<CustomException>()
    .Single(x => x.Value == someEnumValue);
// or CustomException with custom messages
var single = source
    .AllowVerboseException()
    .WithSearchParams(someEnumValue)
    .IfEmpty().Throws<CustomException>("Found nothing in the source for " + someEnumValue)
    .IfMoreThanOne().Throws<CustomException>("Found more than one in the source for " + someEnumValue)
    .Single(x => x.Value == someEnumValue);
//抛出泛型但详细的InvalidOperationException,如
//SomeType类型的序列不包含搜索条件值为SomeEnum.Value的匹配元素
var单=源
.AllowVerboseException()
.WithSearchParams(someEnumValue)
.Single(x=>x.Value==someEnumValue);
//或自定义例外
var单=源
.AllowVerboseException()
.WithSearchParams(someEnumValue)
.IfEmpty().Throws()
.ifmore().Throws()
.Single(x=>x.Value==someEnumValue);
//或带有自定义消息的自定义异常
var单=源
.AllowVerboseException()
.WithSearchParams(someEnumValue)
.IfEmpty().Throws(“在“+someEnumValue”的源中找不到任何内容)
.ifmorethane().Throws(“在“+someEnumValue”的源中找到多个)
.Single(x=>x.Value==someEnumValue);
fluent断言解决方案允许

  • 倾销项目(之前必须列举顺序)
  • 自定义消息的延迟加载(通过传递Func而不是字符串)
  • 在不调用单个/第一个方法的情况下验证假设(完全替换if-else)(毕竟调用Verify方法.IfEmpty().Throws和/或.IfMoreThanOne().Throws)
  • 处理任何案件
代码(和单元测试)在这里提供,但我要强调主要的逻辑

private const int MoreThanOne = 2;
...
public T SingleOrDefault(Func<T, bool> predicate = null)
{
    if (predicate != null)
        this.sequence = this.sequence.Where(predicate);

    return this.Get(Only.Single | Only.Default);
}
...
private T Get(Only only)
{
    // the main trip and probably performance downgrade
    // the logic takes first 2 elements to then check IfMoreThanOne
    // it might be critical in DB queries but might be not
    var items = this.sequence.Take(MoreThanOne).ToList();
    switch (items.Count)
    {
        case 1:
        case MoreThanOne when only.HasFlag(Only.First):
            var first = items.First();
            this.Dispose();
            return first;
        case 0 when only.HasFlag(Only.Default):
            this.Dispose();
            return default(T);
    }

    if (this.ifEmptyExceptionFunc == null) this.ifEmptyExceptionFunc = DefaultExceptionFunc;
    if (this.ifMoreThanOneExceptionFunc == null) this.ifMoreThanOneExceptionFunc = DefaultExceptionFunc;

    this.Verify(() => items.Count);
    throw new NotSupportedException("Should not reach this code");
}

private void Verify(Func<int> getItemCount)
{
    var itemCount = getItemCount.InitLazy();

    ExceptionFunc exceptionFunc = null;

    string message = null;
    if (this.ifEmptyExceptionFunc != null && itemCount.Value == 0)
    {
        message = Messages.Elements.NoOne;
        exceptionFunc = this.ifEmptyExceptionFunc;
    }
    else if (this.ifMoreThanOneExceptionFunc != null && itemCount.Value > 1)
    {
        message = Messages.Elements.MoreThanOne;
        exceptionFunc = this.ifMoreThanOneExceptionFunc;
    }
    else if (this.ifAnyExceptionFunc != null && itemCount.Value > 0)
    {
        message = Messages.Elements.Some;
        exceptionFunc = this.ifAnyExceptionFunc;
    }

    if (exceptionFunc == null)
        return;

    message = string.Format(Messages.BeginningFormat, this.typeNameOverride ?? typeof(T).
    this.searchCriteria = this.searchCriteria ?? this.searchCriteriaFunc?.Invoke();
    if (!string.IsNullOrWhiteSpace(this.searchCriteria))
    {
        message += $" with the search criteria {this.searchCriteria}";
    }

    if (this.dumpItemFunc != null)
    {
        message += ". Items: " + this.dumpItemFunc();
    }

    try
    {
        throw exceptionFunc(message);
    }
    finally
    {
        this.Dispose();
    }
}

private const int不止一个=2;
...
公共T SingleOrDefault(Func谓词=null)
{
if(谓词!=null)
this.sequence=this.sequence.Where(谓词);
返回此.Get(Only.Single | Only.Default);
}
...
私人T Get(仅限)
{
//主行程和可能的性能降级
//逻辑先取前2个元素,然后检查多个元素
//它在数据库查询中可能很重要,但可能不是
var items=this.sequence.Take(不止一个).ToList();
开关(项目数)
{
案例1:
仅当.HasFlag(仅.First)时,情况不止一个:
var first=items.first();
这个。Dispose();
先返回;
仅当.HasFlag(仅.Default)时为案例0:
这个。Dispose();
返回默认值(T);
}
如果(this.ifEmptyExceptionFunc==null)this.ifEmptyExceptionFunc=DefaultExceptionFunc;
如果(this.ifMoreThanOneExceptionFunc==null)this.ifMoreThanOneExceptionFunc=DefaultExceptionFunc;
验证(()=>items.Count);
抛出新的NotSupportedException(“不应到达此代码”);
}
私有无效验证(Func getItemCount)
{
var itemCount=getItemCount.InitLazy();
ExceptionFunc ExceptionFunc=null;
字符串消息=null;
if(this.ifEmptyExceptionFunc!=null&&itemCount.Value==0)
{
message=Messages.Elements.NoOne;
exceptionFunc=this.ifEmptyExceptionFunc;
}
else if(this.ifMoreThanOneExceptionFunc!=null&&itemCount.Value>1)
{
message=Messages.Elements.more;
exceptionFunc=this.ifmorethaneoExceptionFunc;
}
else if(this.ifAnyExceptionFunc!=null&&itemCount.Value>0)
{
message=Messages.Elements.Some;
exceptionFunc=this.ifAnyExceptionFunc;
}
if(exceptionFunc==null)
回来
message=string.Format(Messages.beginingformat,this.typeNameOverride??typeof(T)。
this.searchCriteria=this.searchCriteria??this.searchCriteriaFunc?.Invoke();
如果(!string.IsNullOrWhiteSpace(this.searchCriteria))
{
带有搜索条件{this.searchCriteria}的消息+=$”;
}
如果(this.dumpItemFunc!=null)
{
message+=”.Items:“+this.dumpItemFunc();
}
尝试
{
抛出异常函数(消息);
}
最后
{
这个。Dispose();
}
}

当您仅使用
Single()
时,您肯定希望
private const int MoreThanOne = 2;
...
public T SingleOrDefault(Func<T, bool> predicate = null)
{
    if (predicate != null)
        this.sequence = this.sequence.Where(predicate);

    return this.Get(Only.Single | Only.Default);
}
...
private T Get(Only only)
{
    // the main trip and probably performance downgrade
    // the logic takes first 2 elements to then check IfMoreThanOne
    // it might be critical in DB queries but might be not
    var items = this.sequence.Take(MoreThanOne).ToList();
    switch (items.Count)
    {
        case 1:
        case MoreThanOne when only.HasFlag(Only.First):
            var first = items.First();
            this.Dispose();
            return first;
        case 0 when only.HasFlag(Only.Default):
            this.Dispose();
            return default(T);
    }

    if (this.ifEmptyExceptionFunc == null) this.ifEmptyExceptionFunc = DefaultExceptionFunc;
    if (this.ifMoreThanOneExceptionFunc == null) this.ifMoreThanOneExceptionFunc = DefaultExceptionFunc;

    this.Verify(() => items.Count);
    throw new NotSupportedException("Should not reach this code");
}

private void Verify(Func<int> getItemCount)
{
    var itemCount = getItemCount.InitLazy();

    ExceptionFunc exceptionFunc = null;

    string message = null;
    if (this.ifEmptyExceptionFunc != null && itemCount.Value == 0)
    {
        message = Messages.Elements.NoOne;
        exceptionFunc = this.ifEmptyExceptionFunc;
    }
    else if (this.ifMoreThanOneExceptionFunc != null && itemCount.Value > 1)
    {
        message = Messages.Elements.MoreThanOne;
        exceptionFunc = this.ifMoreThanOneExceptionFunc;
    }
    else if (this.ifAnyExceptionFunc != null && itemCount.Value > 0)
    {
        message = Messages.Elements.Some;
        exceptionFunc = this.ifAnyExceptionFunc;
    }

    if (exceptionFunc == null)
        return;

    message = string.Format(Messages.BeginningFormat, this.typeNameOverride ?? typeof(T).
    this.searchCriteria = this.searchCriteria ?? this.searchCriteriaFunc?.Invoke();
    if (!string.IsNullOrWhiteSpace(this.searchCriteria))
    {
        message += $" with the search criteria {this.searchCriteria}";
    }

    if (this.dumpItemFunc != null)
    {
        message += ". Items: " + this.dumpItemFunc();
    }

    try
    {
        throw exceptionFunc(message);
    }
    finally
    {
        this.Dispose();
    }
}