Scott Hanselman

Example Code - Opinionated ContosoUniversity on ASP.NET Core 2.0's Razor Pages

July 25, '18 Comments [13] Posted in ASP.NET | DotNetCore | Open Source
Sponsored By

The best way to learn about code isn't just writing more code - it's reading code! Not all of it will be great code and much of it won't be the way you would do it, but it's a great way to expand your horizons.

In fact, I'd argue that most people aren't reading enough code. Perhaps there's not enough clean code bases to check out and learn from.

I was pleased to stumble on this code base from Jimmy Bogard called Contoso University at https://github.com/jbogard/ContosoUniversityDotNetCore-Pages.

There's a LOT of good stuff to read in this repo so I won't claim to have read it all or as deeply as I could. In fact, there's a good solid day of reading and absorbing here.However, here's some of the things I noticed and that I appreciate. Some of this is very "Jimmy" code, since it was written for and by Jimmy. This is a good thing and not a dig. We all collect patterns and make libraries and develop our own spins on architectural styles. I love that Jimmy collects a bunch of things he's created or contributed to over the years and put it into a nice clear sample for us to read. As Jimmy points out, there's a lot in https://github.com/jbogard/ContosoUniversityDotNetCore-Pages to explore:

Clone and Build just works

A low bar, right? You'd be surprised how often I git clone someone's repository and they haven't tested it elsewhere. Bonus points for a build.ps1 that bootstraps whatever needs to be done. I had .NET Core 2.x on my system already and this build.ps1 got the packages I needed and built the code cleanly.

It's an opinioned project with some opinions. ;) And that's great, because it means I'll learn about techniques and tools that I may not have used before. If someone uses a tool that's not the "defaults" it may me that the defaults are lacking!

  • Build.ps1 is using a build script style taken from PSake, a powershell build automation tool.
  • It's building to a folder called ./artifacts as as convention.
  • Inside build.ps1, it's using Roundhouse, a Database Migration Utility for .NET using sql files and versioning based on source control http://projectroundhouse.org
  • It's set up for Continuous Integration in AppVeyor, a lovely CI/CD system I use myself.
  • It uses the Octo.exe tool from OctopusDeploy to package up the artifacts.

Organized and Easy to Read

I'm finding the code easy to read for the most part. I started at Startup.cs to just get a sense of what middleware is being brought in.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMiniProfiler().AddEntityFramework();

    services.AddDbContext<SchoolContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddAutoMapper(typeof(Startup));

    services.AddMediatR(typeof(Startup));

    services.AddHtmlTags(new TagConventions());

    services.AddMvc(opt =>
        {
            opt.Filters.Add(typeof(DbContextTransactionPageFilter));
            opt.Filters.Add(typeof(ValidatorPageFilter));
            opt.ModelBinderProviders.Insert(0, new EntityModelBinderProvider());
        })
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
        .AddFluentValidation(cfg => { cfg.RegisterValidatorsFromAssemblyContaining<Startup>(); });
}
Here I can see what libraries and helpers are being brought in, like AutoMapper, MediatR, and HtmlTags. Then I can go follow up and learn about each one.

MiniProfiler

I've always loved MiniProfiler. It's a hidden gem of .NET and it's been around being awesome forever. I blogged about it back in 2011! It sits in the corner of your web page and gives you REAL actionable details on how your site behaves and what the important perf timings are.

MiniProfiler is the profiler you didn't know you needed

It's even better with EF Core in that it'll show you the generated SQL as well! Again, all inline in your web site as you develop it.

inline SQL in MiniProfiler

Very nice.

Clean Unit Tests

Jimmy is using XUnit and has an IntegrationTestBase here with some stuff I don't understand, like SliceFixture. I'm marking this as something I need to read up on and research. I can't tell if this is the start of a new testing helper library, as it feels too generic and important to be in this sample.

He's using the CQRS "Command Query Responsibility Segregation" pattern. Here starts with a Create command, sends it, then does a Query to confirm the results. It's very clean and he's got a very isolated test.

[Fact]
public async Task Should_get_edit_details()
{
    var cmd = new Create.Command
    {
        FirstMidName = "Joe",
        LastName = "Schmoe",
        EnrollmentDate = DateTime.Today
    };

    var studentId = await SendAsync(cmd);

    var query = new Edit.Query
    {
        Id = studentId
    };

    var result = await SendAsync(query);

    result.FirstMidName.ShouldBe(cmd.FirstMidName);
    result.LastName.ShouldBe(cmd.LastName);
    result.EnrollmentDate.ShouldBe(cmd.EnrollmentDate);
}

FluentValidator

https://fluentvalidation.net is a helper library for creating clear strongly-typed validation rules. Jimmy uses it throughout and it makes for very clean validation code.

public class Validator : AbstractValidator<Command>
{
    public Validator()
    {
        RuleFor(m => m.Name).NotNull().Length(3, 50);
        RuleFor(m => m.Budget).NotNull();
        RuleFor(m => m.StartDate).NotNull();
        RuleFor(m => m.Administrator).NotNull();
    }
}

Useful Extensions

Looking at a project's C# extension methods is a great way to determine what the author feels are gaps in the underlying included functionality. These are useful for returning JSON from Razor Pages!

public static class PageModelExtensions
{
    public static ActionResult RedirectToPageJson<TPage>(this TPage controller, string pageName)
        where TPage : PageModel
    {
        return controller.JsonNet(new
            {
                redirect = controller.Url.Page(pageName)
            }
        );
    }

    public static ContentResult JsonNet(this PageModel controller, object model)
    {
        var serialized = JsonConvert.SerializeObject(model, new JsonSerializerSettings
        {
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
        });

        return new ContentResult
        {
            Content = serialized,
            ContentType = "application/json"
        };
    }
}

PaginatedList

I've always wondered what to do with helper classes like PaginatedList. Too small for a package, too specific to be built-in? What do you think?

public class PaginatedList<T> : List<T>
{
    public int PageIndex { get; private set; }
    public int TotalPages { get; private set; }

    public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
    {
        PageIndex = pageIndex;
        TotalPages = (int)Math.Ceiling(count / (double)pageSize);

        this.AddRange(items);
    }

    public bool HasPreviousPage
    {
        get
        {
            return (PageIndex > 1);
        }
    }

    public bool HasNextPage
    {
        get
        {
            return (PageIndex < TotalPages);
        }
    }

    public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageIndex, int pageSize)
    {
        var count = await source.CountAsync();
        var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
        return new PaginatedList<T>(items, count, pageIndex, pageSize);
    }
}

I'm still reading all the source I can. Absorbing what resonates with me, considering what I don't know or understand and creating a queue of topics to read about. I'd encourage you to do the same! Thanks Jimmy for writing this large sample and for giving us some code to read and learn from!


Sponsor: Scale your Python for big data & big science with Intel® Distribution for Python. Near-native code speed. Use with NumPy, SciPy & scikit-learn. Get it Today!

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

AltCover and ReportGenerator give amazing code coverage on .NET Core

July 20, '18 Comments [10] Posted in DotNetCore | Open Source
Sponsored By

I'm continuing to explore testing and code coverage on open source .NET Core. Earlier this week I checked out coverlet. There is also the venerable OpenCover and there's some cool work being done to get OpenCover working with .NET Core, but it's Windows only.

Today, I'm exploring AltCover by Steve Gilham. There are coverage tools that use the .NET Profiling API at run-time, instead, AltCover weaves IL for its coverage.

As the name suggests, it's an alternative coverage approach. Rather than working by hooking the .net profiling API at run-time, it works by weaving the same sort of extra IL into the assemblies of interest ahead of execution. This means that it should work pretty much everywhere, whatever your platform, so long as the executing process has write access to the results file. You can even mix-and-match between platforms used to instrument and those under test.

AltCover is a NuGet package but it's also available as .NET Core Global Tool which is awesome.

dotnet tool install --global altcover.global

This makes "altcover" a command that's available everywhere without adding it to my project.

That said, I'm going to follow the AltCover Quick Start and see how quickly I can get it set up!

I'll Install into my test project hanselminutes.core.tests

dotnet add package AltCover

and then run

dotnet test /p:AltCover=true

90.1% Line Coverage, 71.4% Branch CoverageCool. My tests run as usual, but now I've got a coverage.xml in my test folder. I could also generate LCov or Cobertura reports if I'd like. At this point my coverage.xml is nearly a half-meg! That's a lot of good information, but how do I see  the results in a human readable format?

This is the OpenCover XML format and I can run ReportGenerator on the coverage file and get a whole bunch of HTML files. Basically an entire coverage mini website!

I downloaded ReportGenerator and put it in its own folder (this would be ideal as a .NET Core global tool).

c:\ReportGenerator\ReportGenerator.exe -reports:coverage.xml -targetdir:./coverage

Make sure you use a decent targetDir otherwise you might end up with dozens of HTML files littered in your project folder. You might also consider .gitignoring the resulting folder and coverage file. Open up index.htm and check out all this great information!

Coverage Report says 90.1% Line Coverage

Note the Risk Hotspots at the top there! I've got a CustomPageHandler with a significant NPath Complexity and two Views with a significant Cyclomatic Complexity.

Also check out the excellent branch coverage as expressed here in the results of the coverage report. You can see that EnableAutoLinks was always true, so I only ever tested one branch. I might want to add a negative test here and explore if there's any side effects with EnableAutoLinks is false.

Branch Coverage

Be sure to explore AltCover and its Full Usage Guide. There's a number of ways to run it, from global tools, dotnet test, MSBuild Tasks, and PowerShell integration!

There's a lot of great work here and it took me literally 10 minutes to get a great coverage report with AltCover and .NET Core. Kudos to Steve on AltCover! Head over to https://github.com/SteveGilham/altcover and give it a STAR, file issues (be kind) or perhaps offer to help out! And most of all, share cool Open Source projects like this with your friends and colleagues.


Sponsor: Preview the latest JetBrains Rider with its built-in spell checking, initial Blazor support, partial C# 7.3 support, enhanced debugger, C# Interactive, and a redesigned Solution Explorer.

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

.NET Core Code Coverage as a Global Tool with coverlet

July 18, '18 Comments [5] Posted in DotNetCore | Open Source
Sponsored By

Last week I blogged about "dotnet outdated," an essential .NET Core "global tool" that helps you find out what NuGet package reference you need to update.

.NET Core Global Tools are really taking off right now. They are meant for devs - this isn't a replacement for chocolatey or apt-get - this is more like npm's global developer tools. They're putting together a better way to find and identify global tools, but for now Nate McMaster has a list of some great .NET Core Global Tools on his GitHub. Feel free to add to that list!

.NET tools can be installed like this:

dotnet tool install -g <package id>

So for example:

C:\Users\scott> dotnet tool install -g dotnetsay
You can invoke the tool using the following command: dotnetsay
Tool 'dotnetsay' (version '2.1.4') was successfully installed.
C:\Users\scott> dotnetsay

Welcome to using a .NET Core global tool!

You know, all the important tools. Seriously, some are super fun. ;)

Coverlet is a cross platform code coverage tool that's in active development. In fact, I automated my build with code coverage for my podcast site back in March. I combined VS Code, Coverlet, xUnit, plus these Visual Studio Code extensions

for a pretty nice experience! All free and open source.

I had to write a little PowerShell script because the "dotnet test" command for testing my podcast site with coverlet got somewhat unruly. Coverlet.msbuild was added as a package reference for my project.

dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=./lcov .\hanselminutes.core.tests

I heard last week that coverlet had initial support for being a .NET Core Global Tool, which I think would be convenient since I could use it anywhere on any project without added references.

dotnet tool install --global coverlet.console

At this point I can type "Coverlet" and it's available anywhere.

I'm told this is an initial build as a ".NET Global Tool" so there's always room for constructive feedback.

From what I can tell, I run it like this:

coverlet .\bin\Debug\netcoreapp2.1\hanselminutes.core.tests.dll --target "dotnet" --targetargs "test --no-build"

Note I have to pass in the already-built test assembly since coverlet instruments that binary and I need to say "--no-build" since we don't want to accidentally rebuild the assemblies and lose the instrumentation.

Coverlet can generate lots of coverage formats like opencover or lcov, and by default gives a nice ASCII table:

88.1% Line Coverage in Hanselminutes.core

I think my initial feedback (I'm not sure if this is possible) is smarter defaults. I'd like to "fall into the Pit of Success." That means, even I mess up and don't read the docs, I still end up successful.

For example, if I type "coverlet test" while the current directory is a test project, it'd be nice if that implied all this as these are reasonable defaults.

.\bin\Debug\whatever\whatever.dll --target "dotnet" --targetargs "test --nobuild"

It's great that there is this much control, but I think assuming "dotnet test" is a fair assumption, so ideally I could go into any folder with a test project and type "coverlet test" and get that nice ASCII table. Later I'd be more sophisticated and read the excellent docs as there's lots of great options like setting coverage thresholds and the like.

I think the future is bright with .NET Global Tools. This is just one example! What's your favorite?


Sponsor: Preview the latest JetBrains Rider with its built-in spell checking, initial Blazor support, partial C# 7.3 support, enhanced debugger, C# Interactive, and a redesigned Solution Explorer.

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

NuKeeper for automated NuGet Package Reference Updates on Build Servers

July 10, '18 Comments [9] Posted in DotNetCore | NuGet | Open Source
Sponsored By

Last week I looked at "dotnet outdated," a super useful .NET Core Global Tool for keeping a project's NuGet packages up to date. Since then I've discovered there's a whole BUNCH of great projects solving different aspects of the "minor version problem." I love this answer "Why?" from the NuKeeper (inspired by Greenkeeper) project with emphasis mine. NuKeeper will check for updates AND try to update your references for you! Why not automate the tedious!

NuGet package updates are a form of change that should be deployed, and we likewise want to change the cycle from "NuGet package updates are infrequent and contain lots of package changes, therefore NuGet package updates are hard and dangerous..." to "NuGet package updates are frequent and contain small changes, therefore NuGet package updates are easy and routine...".

Certainly no one is advocating updating the major versions of your dependent NuGet packages, but small compatible bug fixes come often and are often missed. Including a tool to discover - and optionally apply - these changes in a CI/CD (Continuous Integration/Continuous Deployment) pipeline can be a great timesaver.

Why do we deploy code changes frequently but seldom update NuGet packages?

Good question!

NuKeeper

NuKeeper is a .NET tool as well that you can install safely with:

dotnet tool install --global NuKeeper

Here it is running on my regularly updated podcast website that is running ASP.NET Core 2.1:

NuKeeper says I have 3 packages to update

Looks like three of my packages are out of date. NuKeeper shows what version I have and what I could update to, as well as how long an update has been available.

You can also restrict your updates by policy, so "age=3w" for packages over 3 weeks old (so you don't get overly fresh updates) or "change=minor" or "change=patch" if you trust your upstream packages to not break things in patch releases, etc.

NuKeeper is picking up steam and while (as of the time of this writing) its command line parameter style is a little unconventional, Anthony Steele and the team is very open to feedback with many improvements already in progress as this project matures!

The update functionality is somewhat experimental and currently does 1 update per local run, but I'm really enjoying the direction NuKeeper is going!

Automatic NuGet Updates via Pull Request

NuKeeper has a somewhat unique and clever feature called Repository Mode in that it can automatically issue a Pull Request against your repository with the changes needed to update your NuGet references. Check out this example PullRequest!

Anthony - the lead developer - points out that ideally you'd set up NuKeeper to send PRs for you. Automatic PRs are NuKeepers primary goal and use case!

The NuKeeperBot has automatically issued a PR with a list of packages to update

Again, it's early days, but between NuKeeper and "dotnet outdated," I'm feeling more in control of my package references than ever before! What are YOU using?


Sponsor: Scale your Python for big data & big science with Intel® Distribution for Python. Near-native code speed. Use with NumPy, SciPy & scikit-learn. Get it Today!

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

dotnet outdated helps you keep your projects up to date

July 6, '18 Comments [7] Posted in DotNetCore | Open Source
Sponsored By

I've moved my podcast site over to ASP.NET Core 2.1 over the last few months. You might want to follow the saga by checking out some of the recent blog posts.

That's just a few of the posts. Be sure to check out the last several months' posts in the calendar view. Anyway, I've been trying lots of new open source tools and libraries like coverlet for .NET Core Code Coverage, and frankly, keeping my project files and dependencies up to date has sucked.

Npm has "npm outdated" and paket has "paket outdated," why doesn't dotnet Core have this also? Certainly at a macro level there's more things to consider as NuGet would need to find the outdated packages for UWP, C++, and a lot of other project types as well. However if we just focus on .NET Core as an initial/primary use case, Jerrie Pelser has "dotnet outdated" for us and it's fantastic!

Once you've got the .NET Core 2.1 SDK or newer, just install the tool globally with one line:

dotnet tool install --global dotnet-outdated

At this point I'll run "dotnet outdated" on my podcast website. While that's running, let me just point you to https://github.com/jerriep/dotnet-outdated as a lovely example of how to release a tool (no matter how big or small) on GitHub.

  • It has an AppVeyor CI link along with a badge showing you that it's passing its build and test suite. Nice.
  • It includes both a NuGet link to the released package AND a myGet link and badge to the dailies.
  • It's got clear installation and clear usage details.
  • Bonus points of screenshots. While not accessible to all, I admit personally that I'm more likely to feel that a project is well-maintained if there are clear screenshots that tell me "what am I gonna get with this tool?"

Here's the initial output on my Site and Tests.

dotnet outdated

After updating the patch versions, here's the output, this time as text. For some reason it's not seeing Coverlet's NuGet so I'm getting a "Cannot resolve latest version" error but I haven't debugged that yet.

» hanselminutes.core.tests
  [.NETCoreApp,Version=v2.1]
  Microsoft.AspNetCore.Mvc.Testing 2.1.1
  Microsoft.NET.Test.Sdk 15.7.2
  Selenium.Support 3.13.1
  Selenium.WebDriver 3.13.1
  Xunit.SkippableFact 1.3.6
  coverlet.msbuild 2.0.1 Cannot resolve latest version
  xunit 2.3.1
  xunit.runner.visualstudio 2.3.1

» hanselminutes.core
  [.NETCoreApp,Version=v2.1]
  BuildBundlerMinifier 2.8.391
  LazyCache 2.0.0-beta03
  LazyCache.AspNetCore 2.0.0-beta03
  Markdig 0.15.0
  Microsoft.ApplicationInsights.AspNetCore 2.4.0-beta2
  Microsoft.AspNetCore.App 2.1.1
  Microsoft.Extensions.Http.Polly 2.1.1
  Microsoft.NET.Test.Sdk 15.7.2

As with all projects and references, while things aren't *supposed* to break when you update a Major.Minor.Patch/Revision.build...things sometimes do. You should check your references and their associated websites and release notes to confirm that you know what's changed and you know what changes you're bringing in.

Shayne blogged about dotnet out-dated and points out the -vl (version lock) options that allows you to locking on Major or Minor versions. No need to take things you aren't ready to take.

All in all, a super useful tool that you should install TODAY.

dotnet tool install --global dotnet-outdated

The source is up at https://github.com/jerriep/dotnet-outdated if you want to leave issues or get involved.


Sponsor: Check out dotMemory Unit, a free unit testing framework for fighting all kinds of memory issues in your code. Extend your unit testing with the functionality of a memory profiler!

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
Previous Page Page 2 of 16 in the DotNetCore category Next Page

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