The SingleOrDefault()
method is great because it doesn't throw an exception if the collection you're calling it against is empty. However, sometimes what I want is to get a new object of some type if nothing exists. For example it would be great if I could do the following:
var client = db.Clients
.Where(c => c.Name == "Some Client")
.SingleOrNew<Client>();
This way I don't have to check if it's null
and if it is create a new one, I always know that my client
object will be something I can use right away.
Any ideas?
SingleOrDefault() – Same as Single(), but it can handle the null value. First() - There is at least one result, an exception is thrown if no result is returned. FirstOrDefault() - Same as First(), but not thrown any exception or return null when there is no result.
Single : It returns a single specific element from a collection of elements if element match found. An exception is thrown, if none or more than one match found for that element in the collection. SingleOrDefault: It returns a single specific element from a collection of elements if element match found.
When you want a default value is returned if the result set contains no record, use SingleOrDefault. When you always want one record no matter what the result set contains, use First or FirstOrDefault. When you want a default value if the result set contains no record, use FirstOrDefault.
SingleOrDefault<TSource>(IEnumerable<TSource>, TSource) Returns the only element of a sequence, or a specified default value if the sequence is empty; this method throws an exception if there is more than one element in the sequence.
Really, you just want to use the null coalescing operator here. Example:
var client = db.Clients
.Where(c => c.Name == "Some Client")
.SingleOrDefault() ?? new Client();
This will return whatever SingleOrDefault returns, except that if SingleOrDefault returns null, the expression returns new Client() instead.
Edit: As John Skeet pointed out, this solution doesn't differentiate between the situation where there is no match and a null element is found, though clearly this is not necessarily a problem in many cases. An alternative is to create an extension method as follows.
public static T SingleOrNew<T>(this IEnumerable<T> query) where T : new()
{
try
{
return query.Single();
}
catch (InvalidOperationException)
{
return new T();
}
}
I would say this is probably the most elegant solution that works in the general case.
If all you want to accomplish is to override the default value (and return a new object), you may be able to do so by using the DefaultIfEmpty() Method before calling SingleOrDefault(). Something like:
var client = db.Clients
.Where(c => c.Name == name)
.DefaultIfEmpty(new Client { Name = name })
.SingleOrDefault();
Would this do? EDIT Turns out that ?? won't work with the generic type.
public static class IEnumerableExtensions
{
public static T SingleOrNew<T>( this IEnumerable<T> query ) where T : new()
{
var value = query.SingleOrDefault();
if (value == null)
{
value = new T();
}
return value;
}
}
Sounds like it could be done, yes. Can't say I remember having been in a situation where I'd use it myself, but it's easy enough to implement. Something like this:
public static T SingleOrNew<T>(this IEnumerable<T> source) where T : new()
{
if (source == null)
{
throw new ArgumentNullException("source");
}
using (IEnumerator<T> iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
return new T();
}
T first = iterator.Current;
if (iterator.MoveNext())
{
throw new InvalidOperationException();
}
return first;
}
}
I'll add it to the list of operators to include in MoreLinq...
Another, more general approach would be to provide a delegate which would only be evaluated if necessary (I need a better name here, admittedly):
public static T SingleOrSpecifiedDefault<T>(this IEnumerable<T> source,
Func<T> defaultSelector)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (defaultSelector == null)
{
throw new ArgumentNullException("defaultSelector");
}
using (IEnumerator<T> iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
return defaultSelector();
}
T first = iterator.Current;
if (iterator.MoveNext())
{
throw new InvalidOperationException();
}
return first;
}
}
Why don't you add it via an Extension method then? Sounds like it would be handy.
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