I have a method which can be written pretty neatly through method chaining:
return viewer.ServerReport.GetParameters()
.Single(p => p.Name == Convention.Ssrs.RegionParamName)
.ValidValues
.Select(v => v.Value);
However I'd like to be able to do some checks at each point as I wish to provide helpful diagnostics information if any of the chained methods returns unexpected results.
To achieve this, I need to break up all my chaining and follow each call with an if
block. It makes the code a lot less readable.
Ideally I'd like to be able to weave in some chained method calls which would allow me to handle unexpected outcomes at each point (e.g. throw a meaningful exception such as new ConventionException("The report contains no parameter")
if the first method returns an empty collection). Can anyone suggest a simple way to achieve such a thing?
Edit:
This is the result of using @JeffreyZhao's answer:
return viewer.ServerReport.GetParameters()
.Assert(result => result.Any(), "The report contains no parameter")
.SingleOrDefault(p => p.Name == Convention.Ssrs.RegionParamName)
.Assert(result => result != null, "The report does not contain a region parameter")
.ValidValues
.Select(v => v.Value)
.Assert(result => result.Any(), "The region parameter in the report does not contain any valid value");
Maybe you can use this approach.
static T Check<T>(this T value)
{
if (...) throw ...;
return value;
}
then:
xxx.Single(...).Check().Select(...).Check()...
Update:
You can even:
static T Validate<T>(this T value, Func<T, bool> validate, string errorMessage)
{
if (!validate(value))
throw new ValidationFailedException(errorMessage);
return value;
}
then:
xxxx.Single()
.Validate(v => v > 0, "Must be greater than zero")
.NextStep()
.Validate(...);
You can easily split the process in separate steps with local variables:
var result1 = viewer.ServerReport.GetParameters();
var result2 = result1.Single(p => p.Name == Convention.Ssrs.RegionParamName);
var result3 = result2.ValidValues;
var result4 = result3.Select(v => v.Value);
return result4;
Now you can do any checks you want between the steps.
Note however that some results doesn't actually do any work. The last step for example doesn't produce a list as result, it produces an enumerable that reads from ValidValues
, so any errors in that step would happen when you use the result, not inside this method. You might want to add .ToList()
at the end of some steps to realise the result.
Consider using of Code Contracts (e.g. add post-condition to ServerReport.GetParameters
to ensure that methods doesn't return empty collection). They allow to do what you want in more elegant way, than writing your own checking logic.
class ReportParameter { }
class ServerReport
{
public ReportParameter[] GetParameters()
{
Contract.Ensures(Contract.Result<ReportParameter[]>() != null && Contract.Result<ReportParameter[]>().Length > 0,
Resource1.Oops);
// here's some logic to build parameters array...
return new ReportParameter[0];
}
}
Usage:
// Oops! I need at least one parameter!
var parameters = new ServerReport().GetParameters();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With