Where are the Authentication views in AspNet Core App?

I am struggling with Visual Studio for Mac. I have been a Visual Studio user since it came in multiple CDs in a box. Visual Studio has been improved a lot, I like it more now than I did before. But there is a problem, the VS version for the Mac is a completely different product. I am both a Mac and PC user, I like both operating systems, Windows and Mac OS. However, I have been trying to use my personal laptop (MacBook Pro M1) for my personal software projects, and a lot of them were initially created with VS for Windows.

Visual Studio for Mac lacks some important things I use in my projects, for example, SQL Server Data Tools (SSDT) projects. I know that this is because we cannot install SQL in a Mac, and also know that there are alternatives to SSDT, like Azure Data Studio… but guess what? It’s not compatible with the ARM architecture which is what Apple’s new chip (M1) uses, and that’s the laptop I have 😕.

Anyways, that’s a topic for another post, this is about finding the Authentication views in an ASP.NET Core app. Since I couldn’t use this MacBook to continue working on my existing projects, I decided to use it only for new ones. That should be fine. So I have an idea for a book-sharing app, and I wanted to use VS for Mac to create this web app. I started with one of the templates and in less than a minute I had a working web app on my laptop. Next, I decided to scaffold the Authentication stuff to get authentication features and allow users to do stuff like the following:

  • Register
  • Login/Logout
  • Forgot Password
  • Reset Password
  • Confirm Email
  • Etc.

I ran the following commands in my Terminal and on my project’s root directory to add the ability to scaffold the identity stuff. You might not need to do this if you have already installed the aspnet codegenerator:

dotnet tool install -g dotnet-aspnet-codegenerator

dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Identity.UI
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

dotnet aspnet-codegenerator identity -h

After this, I ran the following command to add the Authentication features to my project. The following command needs to be run inside your project’s directory:

dotnet aspnet-codegenerator identity -dc ReadMoreBooks.Data.ApplicationDbContext --files "Account.Register;Account.Login;Account.Logout"

After doing this, I didn’t see many changes in my solution, but when I ran my application again, I noticed I had the option to register, login, and logout. I registered a test user, and everything worked fine. Until I wanted to edit the page where a user confirms registration and also the page to reset the password. I was expecting to see ConfirmEmail and ResetPassword views under Areas > Identity > Pages > Account – but they weren’t there, only Register, Login, and Logout.

Where are they? After doing some research and spending a good amount of time searching for them in my solution, I found out that Microsoft hid these views to make projects smaller and more organized. This is the blog post where they announced that and that I clearly missed: https://devblogs.microsoft.com/dotnet/aspnetcore-2-1-identity-ui/

So, it turns out that by using the command line to scaffold these views I was already halfway from where I needed to be. What I mean by that is, by using the command tool (I think this is available through the UI but only in VS for Windows), all I had to do was either not specify which views I wanted to scaffold or specify all the views I wanted to include in my project so I could customize them.

The first command is what I ran initially, which helped create the Register, Login, and Logout views, and while everything else within the Authentication was available, the actual files were hidden.

dotnet aspnet-codegenerator identity -dc ReadMoreBooks.Data.ApplicationDbContext --files "Account.Register;Account.Login;Account.Logout"

So what I ran after was the following command, which added the views I wanted to my project so I could access them and customize them as much as I needed.

dotnet aspnet-codegenerator identity -dc ReadMoreBooks.Data.ApplicationDbContext --files "Account.ConfirmEmail;Account.ForgotPassword;Account.ForgotPasswordConfirmation;Account.Lockout;Account.LoginWith2fa;Account.ResetPassword;Account.ResetPasswordConfirmation"

That’s it! As soon as the command above was executed, all the authentication-related views appeared in my solution, just like in the old times. Perfect.

Now, I learned that I also have the option to run the same command without specifying the –files command, which will then add all of the available views related to Authentication to your project.

dotnet aspnet-codegenerator identity -dc ReadMoreBooks.Data.ApplicationDbContext

So there you have it folks, a feature by Microsoft was a bug for me, at least for a while, until I understood what was happening, and then I learned how to navigate through the changes and get what I wanted.

If you want to learn more about the scaffolding options and how you can use them in your projects, check out this page from Microsoft: https://docs.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-6.0&tabs=netcore-cli&viewFallbackFrom=aspnetcore-2.1

The moral of the story, read the documentation, keep up to date so you are aware of what’s changing and why, and maybe you’ll save some headaches and be more proficient with your coding. Hope this is useful. Cheers!

Don’t forget about character casing when comparing strings!

There are many issues I’ve experienced during the many, many years I’ve worked as a software developer. But one of the most recurring issues is, without a doubt, the mismatching of words due to character casing.

There are solutions to the character casing mismatch problem. For example, you can make your strings all lower case or upper case before comparing them. There are also many programming languages that have features to help with string comparisons.

The issue is no longer the lack of solutions to avoid this problem. The problem is that these solutions require that you, the developer, be proactive by being alert and aware of case sensitivity when making string comparisons. For example, in C#, you have the StringComparer class, which includes properties like StringComparer.OrdinalIgnoreCase to help you ignore the character’s casing when comparing strings.

As a developer, you have to be alert and know when to ignore character casing. While there are many simple ways and tools built into programming languages, sometimes knowing when to do this might not be obvious.

For example, if you call GroupBy in C# and select the value you want your list to be grouped by, it will consider values such as “Abc” and “ABC” as unique, which might not be what you want to do.

In most cases, if you group a list of items by a specific string value, your intention is probably to treat the same values “Abc” and “ABC” as the same. Therefore, you’ll want to ignore the casing as the values are the same in this context.

Issues like the one with GroupBy in C# can go unnoticed until it causes problems. For example, I ran into this issue and didn’t realize the mistake until I tried to add the values of that grouped list to a dictionary and failed. The dictionary attempted to use the values “Abc” and “ABC” as the dictionary key, but it failed since these aren’t unique.

So what can you do about this? Code defensively. Every time you compare strings, consider character casing sensitivity and avoid it easily by converting all your strings to upper case or lower case before comparing. Second, be aware of the use cases where you are calling a built-in function such as GroupBy or ToDictionary as functions like this might be case-sensitive within your programming language.

With the programming language C#, you can use overloads that explicitly specify the string comparison rules for string comparisons. It works in this language by calling a method overload that has a parameter of type StringComparison.

In the example below, I’ll be using StringComparison.OrdinalIgnoreCase for comparisons for culture-agnostic string matching. The example shows you how not ignoring case sensitivity might give you unexpected results.

Examples in C#

Let’s declare a list of books with author names written using different casing

var books = new List<Book>()
{
new Book { Name = "Programa en donde sea", Author = "Ricardo" },
new Book { Name = "Empieza a programar", Author = "ricardo" },
new Book { Name = "Xyz", Author = "Joe" },
new Book { Name = "Despues de la programacion", Author = "RICARDO" },
new Book { Name = "Blah", Author = "Foo" }
};

Let’s group the list of books by Author, but since we are not doing anything to ignore case sensitivity, the result is not what’s expected – It returns five records instead of three as it treats all variations of the name Ricardo as unique values.

var notAUniqueListOfBooks = books.GroupBy(b => b.Author);

Now let’s group the same list of books by author, but this time let’s add a parameter to make the string comparison case insensitive. The result is only three records, that’s because it treats all the variations of the Author name Ricardo as the same value.

var aUniqueListOfBooks = books.GroupBy(b => b.Author, StringComparer.OrdinalIgnoreCase);

Let’s now create a dictionary from the list of books. This dictionary will use the Author value as the key, and both the book’s name and author as the value. The result is five items in the dictionary, again, because it treats the each instance of the author name Ricardo as a unique value due to the difference in casing.

var notAUniqueBookDictionary = books.ToDictionary(b => b.Author, b => b);

Finally, we’ll try to create a dictionary following the same attributes above, but this time, we’ll pass the parameter StringComparer.OrdinalIgnoreCase to make sure the comparison is case insensitive.

The result if this last one is an error with the following message:

“An item with the same key has already been added. Key: ricardo”

This is because since we are ignoring the casing in Author, we cannot create a dictionary as the key values are required to be unique and by ignoring the case of the different variations of the value Ricardo, these are no longer unique. They all end up being the same exact value.

var aUniqueBookDictionary = books.ToDictionary(b => b.Author, b => b, StringComparer.OrdinalIgnoreCase);

Finally, using the examples above, if you wanted to group by Author, and then create a list of all of their books including the name and author values then you could try using ToLookup, and pass the StringComparer parameter to make sure the string comparison in case insensitive.

var aUniqueLookup = books.ToLookup(b => b.Author, b => b, StringComparer.OrdinalIgnoreCase);

The above will give you a dictionary where the key is the Author name and the value is a list of books including name and author. Also, by passing the StringComparer.OrdinalIgnoreCase parameter, we are making sure that the result is a unique list of values.

This the result of our book list when converted into a Lookup object in C#. There are three keys, all unique, and under each key we have a list of books that corresponds to the book’s author representing the Key value.

I hope this is useful, the code I used to test the examples above is all available here if you want to play with it and explore changing the values, parameters, etc. Cheers and happy coding!

Mac OS: xcrun: error: invalid active developer path

While watching one of the Microsoft Build 2021 sessions, I ran into the following error message while trying to clone a project from Github using the git clone command line on my Mac. The entire error message is below:

xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun

Thankfully, the fix for this is pretty straightforward, install the Xcode toolkit! I had done this before, but for some reason, I had to do it again, and only after doing this, my git commands started to work while executing them in the terminal.

To install the Xcode toolkit, use the following command in your Mac OS terminal:

xcode-select --install

If that doesn’t work, try the following:

sudo xcode-select --install

And if that also doesn’t work, you can try the following next:

sudo xcode-select --reset

If the command line options above don’t work for you, you can download the Xcode toolkit manually from Apple’s Developer download page.

How to (efficiently) update millions of records in a SQL table

You need to update one or more columns in a table with millions of records with data from another table, what’s a good way of going this with low risk and in an optimized way? One of my favorite ways of dealing with millions of records in a table is processing inserts, deletes, or updates in batches.

Updating data in batches of 10,000 records at a time and using a transaction is a simple and efficient way of performing updates on millions of records. The key is to batch out your data to allow you to process a smaller number of records at a time.

Example:

use DatabaseName
go

DECLARE @batchId INT
DECLARE @batchSize INT
DECLARE @results INT

SET @results = 1
SET @batchSize = 10000
SET @batchId = 0

-- when 0 rows returned, exit the loop
WHILE (@results > 0)
	BEGIN
		BEGIN TRAN;

		UPDATE Table1 SET columnA = Table2.columnA
		FROM Table2
		INNER JOIN Table1 ON Table2.Id = Table1.Id
		WHERE Table1.columnA is null
		AND (Table2.Id > @batchId
		AND Table2.Id <= @batchId + @batchSize)

		SET @results = @@ROWCOUNT
	
		-- next batch
		SET @batchId = @batchId + @batchSize

		COMMIT TRAN;
	END

-- the sql below is just to measure the performance of this update, it is not needed to update your data.

DECLARE @startTime DATETIME

SELECT  @startTime = GETDATE()
SET STATISTICS PROFILE ON
SELECT  COUNT(*)
FROM Table1 first OPTION (RECOMPILE)
SET STATISTICS PROFILE OFF
SELECT  'It took ' + CONVERT(VARCHAR(8), DATEDIFF(ms, @startTime, GETDATE())) + ' ms'
GO

In the query above we declare the following variables to do the batching:

  • @results – this variable will hold the number of records updated; when zero, the query will stop. This is to avoid an infinite loop.
  • @batchId – this is set to zero initially, and it is used to compare the table id against it, and after each update, it is set to the id plus the batch size. This allows splitting the millions of records into batches.
  • @batchSize – the number of records to update at a time.

It is important to know that the above algorithm will only work when your table’s primary keys are of type int. If your table’s primary keys are guids, the above will not work.

The section at the end of the query is only used to review the performance of your updates, it is not necessary to update the data and it is a nice way to see how the query performs overall.

I hope the information in this post is helpful, and it helps you learn a simple way to update millions of records in batches, making it easier and lower risk than attempting to do it all at once.

Enabling software engineering teams for success.

Software development is hard, and it isn’t always the programming language or the framework you use, it’s the people who work on it.

People are an essential part of a team; everything can be easily changed and fixed, but you need to make sure people work well together to achieve effective communication and a great culture. In my software development career of over 20 years, the critical difference between successful software projects and failed ones has been how engineering teams are created and how they are allowed to function over time.

Enabling software engineering teams for success.