Scott Hanselman

Clever little C# and ASP.NET Core features that make me happy

June 4, '19 Comments [20] Posted in ASP.NET | DotNetCore
Sponsored By

Visual StudioI recently needed to refactor my podcast site which is written in ASP.NET Core 2.2 and running in Azure. The Simplecast backed API changed in a few major ways from their v1 to a new redesigned v2, so there was a big backend change and that was a chance to tighten up the whole site.

As I was refactoring I made a few small notes of things that I liked about the site. A few were C# features that I'd forgotten about! C# is on version 8 but there were little happinesses in 6.0 and 7.0 that I hadn't incorporated into my own idiomatic view of the language.

This post is collecting a few things for myself, and you, if you like.

I've got a mapping between two collections of objects. There's a list of all Sponsors, ever. Then there's a mapping of shows where a show might have n sponsors.

Out Var

I have to "TryGetValue" because I can't be sure if there's a value for a show's ID. I wish there was a more compact way to do this (a language shortcut for TryGetValue, but that's another post).

Shows2Sponsor map = null;
shows2Sponsors.TryGetValue(showId, out map); if (map != null) { var retVal = sponsors.Where(o => map.Sponsors.Contains(o.Id)).ToList(); return retVal; } return null;

I forgot that in C# 7.0 they added "out var" parameters, so I don't need to declare the map or its type. Tighten it up a little and I've got this. The LINQ query there returns a List of sponsor details from the main list, using the IDs returned from the TryGetValue.

if (shows2Sponsors.TryGetValue(showId, out var map))
    return sponsors.Where(o => map.Sponsors.Contains(o.Id)).ToList();
return null;

Type aliases

I found myself building JSON types in C# that were using the "Newtonsoft.Json.JsonPropertyAttribute" but the name is too long. So I can do this:

using J = Newtonsoft.Json.JsonPropertyAttribute;

Which means I can do this:

[J("description")] 
public string Description { get; set; }

[J("long_description")] public string LongDescription { get; set; }

LazyCache

I blogged about LazyCache before, and its challenges but I'm loving it. Here I have a GetShows() method that returns a List of Shows. It checks a cache first, and if it's empty, then it will call the Func that returns a List of Shows, and that Func is the thing that does the work of populating the cache. The cache lasts for about 8 hours. Works great.

public async Task<List<Show>> GetShows()
{
Func<Task<List<Show>>> showObjectFactory = () => PopulateShowsCache();
return await _cache.GetOrAddAsync("shows", showObjectFactory, DateTimeOffset.Now.AddHours(8));
}
private async Task<List<Show>> PopulateShowsCache()
{
List<Show> shows = shows = await _simpleCastClient.GetShows();
_logger.LogInformation($"Loaded {shows.Count} shows");
return shows.Where(c => c.Published == true && c.PublishedAt < DateTime.UtcNow).ToList();
}

What are some little things you're enjoying?


Sponsor: Manage GitHub Pull Requests right from the IDE with the latest JetBrains Rider. An integrated performance profiler on Windows comes to the rescue as well.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb
Friday, June 07, 2019 4:53:01 AM UTC
In addition to your list I like string interpolation
$"Loaded {shows.Count} shows"
Friday, June 07, 2019 5:48:35 AM UTC
I think, it's just a typo, or is there any more meaningful in this double assignment, I just don't see?
List<Show> shows = shows = await _simpleCastClient.GetShows();
Oliver
Friday, June 07, 2019 7:34:05 AM UTC
I don't think this is a new feature as such, but when in an async method the only await statement is the last statement, the async/await modifiers become unnecessary. So in your example this code:

public async Task<List<Show>> GetShows()
{
Func<Task<List<Show>>> showObjectFactory = () => PopulateShowsCache();
return await _cache.GetOrAddAsync("shows", showObjectFactory, DateTimeOffset.Now.AddHours(8));
}

Can be ever so slightly simplified to this code:

public Task<List<Show>> GetShows()
{
Func<Task<List<Show>>> showObjectFactory = () => PopulateShowsCache();
return _cache.GetOrAddAsync("shows", showObjectFactory, DateTimeOffset.Now.AddHours(8));
}


You can still await GetShows(), but you've saved yourself a whole two words worth of typing.
Ste
Friday, June 07, 2019 7:49:05 AM UTC
@Ste I recently discovered a handy addon for Resharper which highlights such issues:
https://github.com/BigBabay/AsyncConverter

Absolutely great
Christian Sauer
Friday, June 07, 2019 7:56:51 AM UTC
Not quite, Ste. Read this.
Paulo Morgado
Friday, June 07, 2019 8:01:04 AM UTC
This:

Func<Task<List<Show>>> showObjectFactory = () => PopulateShowsCache();


creates a delegate over an anonymous method that invokes PopulateShowsCache.

Whereas

Func<Task<List<Show>>> showObjectFactory = PopulateShowsCache;


just creates a delegate over PopulateShowsCache.
Paulo Morgado
Friday, June 07, 2019 8:43:35 AM UTC
@Christian Sauer - thanks, I already use that extension and find it very useful!
@Paulo Morgado - thanks for that link, it's a fascinating read! I hadn't considered some of those subtleties.
Ste
Friday, June 07, 2019 9:12:10 AM UTC
You're not objective on you first example.
out var makes you gain ONE line of code, not 5 as you show it. You could have written your first code extract like this

Shows2Sponsor map = null;
if (shows2Sponsors.TryGetValue(showId, out map))
return sponsors.Where(o => map.Sponsors.Contains(o.Id)).ToList();
return null;

But that's true: 4 to 3 lines does not seems as important as gaining 5 lines!

And by the way, you're not testing the same thing: in the code block, you were testing if map is null (and it can be null inside your dictionnary), thing that you'r not testing in the second code bloc.

So you should have written the second bloc like that:

shows2Sponsors.TryGetValue(showId, out var map);
if (map != null)
return sponsors.Where(o => map.Sponsors.Contains(o.Id)).ToList();
return null;
Mathieu
Friday, June 07, 2019 9:26:03 AM UTC
Naughty, naughty - methods using async should always be named xxxxAsync() that is the convention that MS instructs and makes sense cause someone using you method then knows they should await it.

I dislike the using 3 = some type example. It obfuscates for no real benefit, intelli-sense is sufficent that you can include the proper type name. Yes you save yourself a 3 or 4 keypresses but you do so at the expense of readability and maintainability. I guess it's fine if you the codebase is your own 1-person-band written but in a team I'd fail that in a code review.

Not new-new - but I love the Elvis operator.

return somethingThatCouldBeNull?.MethodReturningSomething().ToString() ?? string.Empty

instead of:


if (somethingThatCouldBeNull != null)
return somethingThatCouldBeNull.MethodReturningSomething().ToString()
else
return string.Empty

or I guess you could've had:

return (somethingThatCouldBeNull != null)
? somethingThatCouldBeNull.MethodReturningSomething().ToString()
: string.Empty
Peter
Friday, June 07, 2019 9:57:40 AM UTC
Been using String Interpolation and Namespace alias since 2015 I think.
Lov'em!
DioBrando
Friday, June 07, 2019 12:18:30 PM UTC
Isn't returning null for an empty result is poor style?
And if you want to cut down on the line count, replace

if (shows2Sponsors.TryGetValue(showId, out var map))
return sponsors.Where(o => map.Sponsors.Contains(o.Id)).ToList();
return null;


with


return shows2Sponsors.TryGetValue(showId, out var map) ? sponsors.Where(o => map.Sponsors.Contains(o.Id)).ToList() : new List<Sponsor>();



Or switch to APL :-)
Niels Ull Harremoes
Friday, June 07, 2019 1:28:35 PM UTC
One note that doesn't apply to the given example but is still important to know. When using GetOrAdd, if you absolutely/preferably need for the value to be evaluated only once (something that must be unique or is very long to evaluate), you need to
use Lazy in ConcurrentDictionary instead of passing a Func to GetOrAdd
Jonathan
Friday, June 07, 2019 1:36:55 PM UTC
services.AddHttpClient<Something>()
Friday, June 07, 2019 3:30:51 PM UTC
I like that async/await was generalized to be a computation expression. It makes libraries like louthy (brings in functional programming aspects) and superpower (language parser) really nice with how you can write code.
Friday, June 07, 2019 10:16:19 PM UTC
That you can ignore/discard out-arguments with _

return TryUpdate(input, out _);


That you can give tuples names.

public (string Name, string Email) GetNameAndEmail()
{
...
}

// alt 1
var profile = GetNameAndEmail();
var x = profile.Name;

// alt 2
var (name, email) = GetNameAndEmail();
Tore
Monday, June 10, 2019 8:37:05 AM UTC
Специализированный комплекс органических экстрактов и питательных веществ для повышения потенции.
Наш сайт: https://lhqkrwmk.morningeverning.com
Tuesday, June 11, 2019 3:04:48 PM UTC
I LOVE using the string interpolation where you can type variables inline...glad to see I'm not the only one who does this...

Matt
Wednesday, June 12, 2019 4:17:13 AM UTC

public ValueController(IMediator mediator) =>
_mediator = mediator:


Async console apps:


static async Task Main(string[] args)
Wednesday, June 12, 2019 4:54:59 PM UTC
The null conditional operator combined with null coalescing. Especially when receiving an IEnumerable object.



var obj = object?.Property ?? value;

void DoSomething(IEnumerable<Object> objs)
{
if((objs?.Count() ?? 0) > 0)
{
//do something
}
}
Rodney Thomas
Thursday, June 13, 2019 1:29:05 PM UTC

Shows2Sponsor map = null;
shows2Sponsors.TryGetValue(showId, out map);
if (map != null)
{
var retVal = sponsors.Where(o => map.Sponsors.Contains(o.Id)).ToList();
return retVal;
}
return null;


and


if (shows2Sponsors.TryGetValue(showId, out var map))
return sponsors.Where(o => map.Sponsors.Contains(o.Id)).ToList();
return null;


Isn't really the same code. You just removed the null-check, which can be a valid value for a showId (assuming it's a collection of a kind) and the code may run on an NullReferenceException.

Also not sure you are doing yourself a favor using


using J = Newtonsoft.Json.JsonPropertyAttribute;
Which means I can do this:

[J("description")]
public string Description { get; set; }

[J("long_description")]
public string LongDescription { get; set; }


Shorter? Definitely. Readable? Nope.


using AsJson = Newtonsoft.Json.JsonPropertyAttribute;
Which means I can do this:

[AsJson("description")]
public string Description { get; set; }

[AsJson("long_description")]
public string LongDescription { get; set; }


would be better though.

Saving bytes/letter is a Ruby-type of programming. Bad :p
Someone
Name
E-mail
(will show your gravatar icon)
Home page
2+5

Comment (Some html is allowed: a@href@title, b, blockquote@cite, em, i, li, ol, pre, strike, strong, sub, super, u, ul) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Live Comment Preview

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.