Scott Hanselman

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

June 05, 2019 Comment on this post [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
Hosting By
Hosted in an Azure App Service
June 07, 2019 8:53
In addition to your list I like string interpolation
$"Loaded {shows.Count} shows"
June 07, 2019 9:48
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();
June 07, 2019 11:34
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
June 07, 2019 11:49
@Ste I recently discovered a handy addon for Resharper which highlights such issues:
https://github.com/BigBabay/AsyncConverter

Absolutely great
June 07, 2019 11:56
Not quite, Ste. Read this.
June 07, 2019 12:01
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.
June 07, 2019 12:43
@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
June 07, 2019 13:12
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;
June 07, 2019 13:26
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
June 07, 2019 13:57
Been using String Interpolation and Namespace alias since 2015 I think.
Lov'em!
June 07, 2019 16:18
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 :-)
June 07, 2019 17:28
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
June 07, 2019 17:36
services.AddHttpClient<Something>()
June 07, 2019 19:30
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.
Jon
June 08, 2019 2:16
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();
June 10, 2019 12:37
Специализированный комплекс органических экстрактов и питательных веществ для повышения потенции.
Наш сайт: https://lhqkrwmk.morningeverning.com
June 11, 2019 19:04
I LOVE using the string interpolation where you can type variables inline...glad to see I'm not the only one who does this...

June 12, 2019 8:17

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


Async console apps:


static async Task Main(string[] args)
June 12, 2019 20:54
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
}
}
June 13, 2019 17:29

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

Comments are closed.

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