How to correct processing exceptions in linqXml? - c#

I work with Linq xml in C#, but I don't know how to correct processing exception, when some elements is null.
For example, I need get some value of attribute, but this attribute can be null, or can be null some part of path. I do like this:
public static string GetImage(this HtmlNode element)
{
var result = "";
try
{
return result = element.Element("div").Element("a").Element("img")?.GetAttribute("src").Value;
}
catch (Exception)
{
return result;
}
}
Maybe I can do it easy?
Thanks for answer.

If you already are using C# 6.0, then use null conditional operators as you already did after Element("img"):
return element?.Element("div")?.Element("a")?.Element("img")?.GetAttribute("src")?.Value;

Related

Excel IsError equivalent function in C#

Is there a function in c# that can help to check if the statement is going to return error.
I am aware about try{} catch{} but we can't use it in condition checking. For example:
var opertionResult = SomeRestFunction.Trigger();
string ServerResponse = if(operationResult != null)
{
if(operationResult.Response != null)
{
operationResult.Response.ResponseText;
}
Else
{
"Null";
}
}
Else
{ "Null"; }
In the example above, I need to perform multiple checks to validate the operationResult object properties at multiple levels due to nested objects in it, to determine the final value I want to read and consume as server response value.
If we have some thing like IsError() function in Excel, we can simply try to read the final object property i.e. operationResult.Response.ResponseText; inside of that function and if any of the parent object is null and it has to throw an error it will return false and we can return the value from the else block as shown in the hypothetical example below:
var opertionResult = SomeRestFunction.Trigger();
string ServerResponse = IsError(operationResult.Response.ResponseText)? "Null": operationResult.Response.ResponseText;
So, do we have something like this in C#? Hope this makes sense?
If you're using c# 6 or newer version then you can use null-conditional & null-coalescing-operator operator like below.
Here you need to place ? to check if object is null or not. If object is null then it will not perform any further check and return null. And null-coalescing-operator (??) will be used to check if value is null then it will return value provided afer ??.
string ServerResponse = SomeRestFunction.Trigger()?.Response?.ResponseText ?? "Null";

How to test if method returns expected data type?

Is it possible to create a NUnit Test method to check if the method returns the expected data type ?
Here's what I mean:
I've a static string that takes two parameters and checks to see if it matches with another string. If it does the methods simply returns that string.
I want to test to ensure that this method does infact return type of string and any exceptions that might occur.
Sample Code:
public static string GetXmlAttributeValue(this XmlElement element, string attributeName)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
if (attributeName == null)
{
throw new ArgumentNullException("attributeName");
}
string attributeValue = string.Empty;
if (element.HasAttribute(attributeName))
attributeValue = element.Attributes[attributeName].Value;
else
throw new XmlException(element.LocalName + " does not have an attribute called " + attributeName);
return attributeValue;
}
Here's how my solution is looking:
I'd like to write my test code within the TestLibrary class library.
Normally, there is no need to test the return type. C# is statically typed language, so this method cannot return something else different than string.
But If you want to write a test, which will fail if someone changes the return type, you can do something like this:
Assert.That(result, Is.TypeOf<string>());
To test for the return type you can use the Is.TypeOf<yourType> syntax mentioned by #dimitar-tsonev. Here is a list of the supported type contraints:
You also mention that you want to write tests to verify the exceptions. For that you can either use the ExpectedExceptionAttribute attribute as documented here or the exception asserts syntax as documented here.
You Don't Need To Test For the method to return a certain data type because it can only return the specific return type. You could run the method and use an Assert to check it isn't null, then you know the method has returned the correct type.
var result = GetXmlAttributeValue(par1,par2);
Assert.isNotNull(result);
Hope This helps!

Good practice - throw an exception to satisfy a function return

Built a custom IDataReader which goes looking in XML for values that match particular element names and, if found, returns the values. The GetValue function - which has to return an Object, as specified by the interface, looks like this:
public object GetValue(int i)
{
string SearchString = _columns[i];
var searchedAttributeValue = from nm in _el.Attributes(SearchString) select nm;
if (searchedAttributeValue.Count() > 0)
{
return ParseTypes(searchedAttributeValue.First().Value);
}
var searchedElementValue = from nm in _el.Elements(SearchString) select nm;
if (searchedElementValue.Count() > 0)
{
return ParseTypes(searchedElementValue.First().Value);
}
}
This won't build, obviously, because it's possible to reach the end of the function without a valid return.
In this instance, if that happens, it means there's a config error - the user is looking for an element or attribute in the XML that's not there.
I discovered (this was new to me) that you can get round the problem by doing this:
public object GetValue(int i)
{
if (_el == null)
{
_el = XNode.ReadFrom(_reader) as XElement;
}
string SearchString = _columns[i];
var searchedAttributeValue = from nm in _el.Attributes(SearchString) select nm;
if (searchedAttributeValue.Count() > 0)
{
return ParseTypes(searchedAttributeValue.First().Value);
}
var searchedElementValue = from nm in _el.Elements(SearchString) select nm;
if (searchedElementValue.Count() > 0)
{
return ParseTypes(searchedElementValue.First().Value);
}
throw new Exception("oh crap!");
}
So the function doesn't return, it just throws an error.
However, something feels fundamentally wrong with this approach. Is it okay, why is it okay/not okay and is there a better way to handle the situation?
Well unless you really need the catch block, you don't need to introduce try/catch/finally. You can just add a throw statement at the end of your initial method.
Along the way, I'd start using FirstOrDefault(), follow normal C# naming conventions for local variables, stop using query expressions when they're not terribly useful, and throw a more specific exception:
public object GetValue(int i)
{
string searchString = _columns[i];
var searchedAttribute = _el.Attributes(searchString).FirstOrDefault();
if (searchedAttribute != null)
{
return ParseTypes(searchedAttribute.Value);
}
var searchedElement = _el.Elements(searchString).FirstOrDefault();
if (searchedElement != null)
{
return ParseTypes(searchedElement.Value);
}
// Nothing found - oops.
throw new SomeSpecificException("Some message here");
}
If you need the catch block for logging, I'd probably try to catch a specific exception, which probably means moving that into ParseTypes anyway (as you shouldn't get any exceptions from the rest of the calls...) Either way, keep the throw statement at the end.
EDIT: In terms of design, whether you should throw an exception when the value isn't found or not really depends on the expectations. We have no way of knowing whether that indicates something wrong with the system, or just a perfectly normal absence of data. That should determine your choice here. In particular, if every caller is going to try to catch the exception, then that's a design smell and you should either return null or use something like the Dictionary<,>.TryGetValue approach.
I like to make my methods similar to the .NET framework Parse() and TryParse() methods. In your case I would either do:
public object GetValue(int i)
{
// ...
// fail
throw new Exception("Cannot get value");
}
or do:
public bool TryGetValue(int i, out object result)
{
// ...
// fail
result = null;
return false;
}
The try catch is irrelvant. you could simply refactor your code to:
public object GetValue(int i)
{
if (_el == null)
{
_el = XNode.ReadFrom(_reader) as XElement;
}
string SearchString = _columns[i];
var searchedAttributeValue = from nm in _el.Attributes(SearchString) select nm;
if (searchedAttributeValue.Count() > 0)
{
return ParseTypes(searchedAttributeValue.First().Value);
}
var searchedElementValue = from nm in _el.Elements(SearchString) select nm;
if (searchedElementValue.Count() > 0)
{
return ParseTypes(searchedElementValue.First().Value);
}
throw new Exception("oh crap!");
}
this has the same net result.
That said throwing exceptions are expensive (computationaly). If you want this to bomb out (stop processing completly, this is a major problem and it should simply die) then thrown an exception.
If it's simply a way to identify when the if statements are not being met maybe change it to a TryParse type function:
public bool GetValue(int i, out object returnVal)
{
if (_el == null)
{
_el = XNode.ReadFrom(_reader) as XElement;
}
string SearchString = _columns[i];
var searchedAttributeValue = from nm in _el.Attributes(SearchString) select nm;
if (searchedAttributeValue.Count() > 0)
{
returnVal = ParseTypes(searchedAttributeValue.First().Value);
return true;
}
var searchedElementValue = from nm in _el.Elements(SearchString) select nm;
if (searchedElementValue.Count() > 0)
{
returnVal = ParseTypes(searchedElementValue.First().Value);
return true;
}
return false;
}
then
if (GetValue(i, out value))
//success
else
//it's failed.
If it is genuinely an exceptional circumstance that the search won't return anything because the configuration is wrong, then by all means, throw an exception. The problem with this is continuation. Can client code continue if an item isn't found? If it can, client code will need to closely examine any exception thrown from your code to determine if it was thrown because an item didn't exist, or because something else went wrong that can't be continued from.
The other option is to return something that indicates that the value wasn't found, and allow calling code to deal with it. You could return null to indicate to calling code that no item was found, and that might be fine in this instance. A better solution might be to create a simple Optional<T> class, that contains an indication of whether the object existed (perhaps HasValue), and the object itself if it did. Calling code can easily and more concretely check whether an object was returned and deal with a situation where it isn't, and exceptions don't require additional scrutiny.
EDIT: A better alternative altogether might be the Parse and TryParse paradigm suggested by others, but I think this answer might have some use, so I'll leave it here :)
There are quite a few bad practices in this approach: catch (Exception) should always be avoided. Only catch the exceptions you expect there. Also, throw Exception() is bad style. Always throw something more specific. But for what you're looking for: You don't need a finally after all, just place a throw new ArgumentException() or something as the last line of the function. That is good practice, if it is really a programming error if the code ever runs there.

C# ?? null coalescing operator LINQ

I am trying to prevent having NULL values when I parse an XML file to a custom object using LINQ.
I found a great solution for this on Scott Gu's blog, but for some reason it does not work for integers with me. I think I have used the same syntax but it seems I am missing something. Oh and for some reason it works when the node is not empty.
Below is an extract of my code.
List<GrantAgresso> lsResult = (from g in xml.Element("root").Elements("Elementname")
select new GrantAgresso()
{
Year = (int?)g.Element("yearnode") ?? 0,
Subdomain = (string)g.Element("domainnode") ?? ""
}).ToList();
The errormessage is:
Input string was not in a correct format.
If anyone has a clue as to what I'm doing wrong, please help :)
Edit: piece of XML (strange names but it's not by choice)
<Agresso>
<AgressoQE>
<r3dim_value>2012</r3dim_value>
<r0r0r0dim_value>L5</r0r0r0dim_value>
<r7_x0023_province_x0023_69_x0023_V005>0</r7_x0023_province_x0023_69_x0023_V005>
<r7_x0023_postal_code_x0023_68_x0023_V004 />
<r7_x0023_country_x0023_67_x0023_V003>1004</r7_x0023_country_x0023_67_x0023_V003>
<r7_x0023_communitydistrict_x0023_70_x0023_V006>0</r7_x0023_communitydistrict_x0023_70_x0023_V006>
</AgressoQE>
</Agresso>
It is this expression which is throwing:
(int?)g.Element("yearnode")
That's because if the actual value of the element's text node is String.Empty and not null, since the empty string is not a valid format for Int32.Parse, the attempted cast fails.
If the element is missing completely from your XML, this works as you expect, but if there is an empty tag <yearnode/> or <yearnode></yearnode>, you'll get the exception.
The following extension method will return 0 both if the element is not present, the element is empty or it contains a string that cannot be parsed to integer:
public static int ToInt(this XElement x, string name)
{
int value;
XElement e = x.Element(name);
if (e == null)
return 0;
else if (int.TryParse(e.Value, out value))
return value;
else return 0;
}
You could use it like this:
...
Year = g.ToInt("r3dim_value"),
...
Or if you're ready to consider the cost of reflection and to accept the default value of any value type, you may use this extension method:
public static T Cast<T>(this XElement x, string name) where T : struct
{
XElement e = x.Element(name);
if (e == null)
return default(T);
else
{
Type t = typeof(T);
MethodInfo mi = t.GetMethod("TryParse",
BindingFlags.Public | BindingFlags.Static,
Type.DefaultBinder,
new Type[] { typeof(string),
t.MakeByRefType() },
null);
var paramList = new object[] { e.Value, null };
mi.Invoke(null, paramList);
return (T)paramList[1]; //returns default(T), if couldn't parse
}
}
and use it:
...
Year = g.Cast<int>("r3dim_value"),
...
You can add Where operator
.....
.Where(a => ! string.IsNullOrEmpty(a)).ToList();
If year is null or empty string that you will get an "Input string was not in a correct format" exception. You may write an extension method to read values. I haven't tested code below, but it may give you some hints.
public static ReadAs<T>(this XElement el, T defaultValue) {
var v = (string)el; // cast string to see if it is empty
if (string.IsNullOrEmpty(v)) // test
return defaultValue;
return (T)el; // recast to our type.
}
The message Input string was not in a correct format looks like the one thrown by int.parse () so it could be that you have a yearnode with a value (not null) but which cannot be successfully parsed to an integer value.
Something like this may fix it:
List<GrantAgresso> lsResult = (from g in xml.Element("root").Elements("Elementname")
let yearNode = g.Element("yearnode")
select new GrantAgresso
{
Year = string.IsNullOrWhiteSpace(yearNode.Value) ? 0 : int.Parse(yearNode.Value),
Subdomain = g.Element("domainnode").Value
}).ToList();
A couple of things to note:
select new GrantAgresso - you don't need parenthesis for a default constructor with object initializers.
string.IsNullOrWhiteSpace - was introduced in .net 4.0, use string.IsNullOrEmpty if you're on 3.5 or earlier
g.Element("domainnode").Value - will always return a string
if you want a null for Year instead of 0, use (int?)null instead of 0
Your problem is that the cast from XElement to int? uses int.Parse method on the string value of the XElement, which in your case is String.Empty. The following results in the same error:
XElement x = new XElement("yearnode", String.Empty);
int? a = (int?)x; // throws FormatException
You can avoid this by first casting the XElement to string and checking if it is null or empty and only if not doing the cast to int?. To do this, replace
Year = (int?)g.Element("yearnode") ?? 0,
with
Year = !string.IsNullOrEmpty((string)g.Element("yearnode")) ? (int?)g.Element("yearnode") : 0,
Its not pretty and will still throw if the string is otherwise not legal, but it works if you can assume that the element is always an integer or null/empty.

Not all code paths return a value

I am having this Linq To SQL query which is taking Customer Category from database.The CustCategory will be defined already.Here is the query.
public IList<string> GetAccountType()
{
using (var db = new DataClasses1DataContext())
{
var acctype = db.mem_types.Select(account=>account.CustCategory).Distinct().ToList();
if (acctype != null)
{
return acctype;
}
}
}
Currently I am getting an error that Not all code paths return a value.If I am always certain that the value is there in the database then do I need to check for null,If I need to check for null then how do I handle this.
Can anyone help me with this.
Any suggestions are welcome.
Since Enumerable.ToList never returns null (see the Return Value section of the documentation), you can safely remove the if.
EDIT: Note that, no matter what your database contains, acctype will never be null:
If no value is found in the database, the return value will be an empty list (which is different than null).
If one record is found and its value is null, the return value will be a valid list with one entry, whose value is null. Still, the list itself is not null.
What happens if:
if (acctype != null)
Is null? What is your method supposed to return?
You need to return something
This is not about LINQ to SQL, the method GetAccountType() must return IList<string>. You should return return acctype; and then check this returned list later using Any(), something like:
if(GetAccountType.Any()){
//not empty
}
How about something like this for a fairly clean and readable solution?:
(Note, updated: removed the check for null, since it would clearly not have any effect).
public IList<string> GetAccountType()
{
var acctype = new List<string>();
using (var db = new DataClasses1DataContext())
{
acctype = db.mem_types.Select(
account=>account.CustCategory).Distinct().ToList();
}
return acctype;
}
You need to return a value from your function:
public IList<string> GetAccountType()
{
using (var db = new DataClasses1DataContext())
{
var acctype = db.mem_types.Select(account=>account.CustCategory).Distinct().ToList();
if (acctype != null)
{
return acctype;
}
}
return acctype;
}

Categories

Resources