Ask yourself six questions before building the Next Big Thing

You have a great new idea for a great new feature or function or service. Before rushing off to create it or implement it, ask yourself…

Who is this feature/function for?
What are the other ways to solve this problem?
Where is the right place for this feature/function?
When is this important to do?
Why is this important?
How will this be support today, tomorrow, and forever?

We often build things without thinking them through. Be your own worst enemy before tackling something, so you know your future self will agree that you made the right decision (or, at least, the best decision you could make at the time).

Extending DbSet for easy filter expressions

Databases often have entities which are often by a repeated expression. A common one is filtering something by a UserId. In SQL, this looks like:

select * 
from orders
where userid = @userid;

Using Entity Framework, we may write something like this:

dataContext.Orders.Where(x => x.UserId == userId);

But I’d really like to make it more expressive and consistent, like this:

dataContext.Orders.ForUser(userId);

Fortunately, it is possible, with an interface and an extension method.

The interface, which I will call IUserEntity, will expose the common filtered expressions.

public interface IUserEntity
{
	int UserId { get; set; }
	User User { get; set; }
}

Any class that can be filtered by users should inherit this class.

public class Order : IUserEntity
{
	public int Id { get; set; }
	public int UserId { get; set; }
	public User User { get; set; } = null!;
}

Then our expression method will extend any DbSet with a type of IUserEntity to include our extension method, which simply returns a filtered DbSet.

public static class DbSetExtensions
{
	public static IQueryable<T> ForUser<T>(this DbSet<T> userEntities, int? userId) where T : class, IUserEntity
	{
		if (userId.HasValue)
		{
			return userEntities.Where(x => x.UserId == userId.Value);
		}
		return userEntities;
	}
}

Note how in the above I made the userId nullable — this is not required, but it does give you a bit more flexibility.

This can be replicated for any other common filter, which will make your code more expressive and easy to read. If you wanted similar extensions on other elements, such as Lists, you could just make a copy of the extension method for that object type, and the appropriate return types.

Use compiler directives to hide test code

Instead of this:

[HttpGet]
public string Test()
{
    return "Hello";
}

Try doing this:


#if DEBUG
[HttpGet]
public string Test()
{
    return "Hello";
}
#endif

Advantage: the code will only be used in Debug (not Release) builds. Of course, if you’re not paying attention to build types, it won’t help. But it has a shot at keeping your test code out of production.

The Internet: friend or foe?

I stumbled across an interesting article about how your digital life will follow you whether you want it to or not. (Read: I Called Off My Wedding. The Internet Will Never Forget).

My rule has always been, be mindful of what you post; assume that everyone will read it and that it will exist forever. The Internet will hold on to things that you may want to forget, or let go of, or wish never happened, making the process of moving on or moving past something much harder that it was at any other point in human existence.

In the “good ol’ days” you would tell your friends and family, “I don’t want to speak of this any more,” and that would (hopefully) be the end of it. People may gossip behind your back (how humans love to gossip), but it was, for the most part, over. The memories lived in your head, and you were able to remove (most of) the reminders in the physical world around you. Not so any more.

The Internet is the friend who hears everything you say to it, shares it with everyone it knows (and it knows billions of people), and never forgets any of it. It is an amazing friend, great for so many things, useful in so many ways, but with a potential dark side.

PING a sequence of IP addresses from the Command Prompt

Have you ever needed to find a free IP address in a given range? Or, have you ever needed to find out which IP addresses in a range are in use? Sure, you can type a bunch of PING commands and note the results. Or, you can loop it:

for /l %i in (96,1,111) do @ping -n 1 10.1.0.%i | find "bytes=" 

Simply replace the “96” and “111” with the start/end range, and “10.1.0” with the subnet of your choosing. The “1” in the range is the counter, so if you want to skip values, simply change this number. The output will only list those devices which receive a reply from the PING.

If you have multiple subnets, you can loop the loop, like this:

Or, if you want it all in a fancy batch file, paste the following into a batch file (I call it “pingall.bat”).

@echo off
if "%3" EQU ""  (
    echo pingall.bat ^<networkip^> ^<startip^> ^<endip^> [step]
    goto :eof
)
setlocal
set networkip=%1
set startip=%2
set endip=%3
set step=%4
if "%step%" EQU "" set step=1
for /l %%i in (%startip%,%step%,%endip%) do ping -n 1 %networkip%.%%i | find "bytes="
endlocal

To run this, you specify the network (class C only in this example), the start IP, the end IP, and optionally the step (which defaults to 1). Your execution will look something like this.

C:\utils>pingall.bat 10.1.2 211 215
Reply from 10.1.2.214: bytes=32 time<1ms TTL=128
Reply from 10.1.2.215: bytes=32 time=2ms TTL=64

It’s a quick way to see what’s online in an IP range — and sometimes, that’s exactly what you are looking for.

Limitations of technology and resources are not “inherently cruel”

In her commentary on speech recognition software (“Speech Recognition Tech Is Yet Another Example of Bias“, Scientific American, Oct 2020), author Claudia Lopez-Llorenda derides the limits of technology because of her need to alter her speech pattern to a non-accented version of her own voice in order to be recognized fluently. In her words, “[changing] such an integral part of an identity to be able to be recognized is inherently cruel.”

The technology community is a scientific one, subject to the same limitations of other sciences: those of resources, knowledge, and capabilities. It is also subject to the same limitations of economics: funding and supply and demand. To imply that technology (and technology companies) are ignoring smaller demographic groups or populations for reasons other than those limitations is short-sighted, and ignores the complexity of the problem and the allocation of available resources to solve it.

Speech recognition has become mainstream, and we have seen solutions delivered to market in the past ten years that were likely considered science fiction 20 years ago. It is still far from a complete solution, and that is shown by the continued rapid advances and developments in the field. Apple, Google, Amazon, and others have brought speech recognition to dozens of languages in just a few years, delivering an imperfect solution to a complex problem that consumers today expected to work without fail, much like we expect our cars to work when we turn the key.

The difference, of course, is that cars are all the same; people are all different, and even though many of us speak similarly, many of us do not, as we use dialects of the same root language — and that is where the economics of science come to play. When you want to implement language support, you will start with the baseline language, to capture the widest population of potential users. As technology improvements come, the availability to pick up smaller and smaller populations of users who speak in dialects will come with it. These are not limitations built into a solution; rather, they are limitations based on technological capabilities and available resources to implement them.

When companies decide to do this, it is not to be exclusive; rather, it is to be inclusive of as many people as possible. Turn on the television in the US and watch the news, and you will largely see newscasters speaking in a standard form of American English. This is by design; they are speaking in a way that people with nearly any dialect or understanding of spoken English can follow, thereby being inclusive of the most number of people by resorting to a common baseline. Technology companies do the same. This decision does not take away anyone’s cultural identity, nor should it be seen as “inherently cruel”.

Over the next ten years, Ms. Lopez-Llorenda will undoubtedly see incredible advances in speech recognition. As technological capabilities grow, and as company resources are freed up by completion of tasks for larger populations of users, she will see improvements in language support (including dialects) and eventually will see that speech recognition is able to recognize an individual’s speech (for example, picking up the l in salmon, for the one person I met in my life who pronounced it that way). The inherent cruelty will go away, not because anyone felt it was cruel, but simply because technology has caught up.

In the meantime, I will continue, at times, to mask my New York dialect in conversations — not because I am trying to hide my cultural identity. Rather, perhaps I am trying to be inclusive of the listener, who may be unfamiliar with such a dialect; or perhaps I don’t want to come across as a paisano — because, after all, if you heard me ordering a cup of cawfee (milk, no sugah), you probably would quickly make a certain opinion of who I am. And that opinion may be right or wrong, and you are entitled to make it, and I don’t take it as an insult. You’re merely taking in speech, and making a decision based on the limited amount of data and information you have to process it — which is, ironically, the same thing Siri is doing.

Create a Windows shortcut to map and open a network drive

After all these years, users still love their drive letters. And inevitably, I still hear the complaint, “My X: drive has disappeared!” Which makes me wonder a few things.

  1. What does the X: drive point to?
  2. Why are we still using drive letters?

It’s not my place to change the world, but we can make it easier on ourselves and our users. So when you have a user who uses network shares mapped to drive letters, create a shortcut on their desktop, and use the following command line in the shortcut — just specify your desired drive letter and your server and share information.

cmd /c "net use x: \\servername\sharename /persistent:yes | explorer x:"

This will both re-map the drive letter if it isn’t mapped, and open a Windows Explorer window to the drive letter. You now put the power in the hands of the user, and free up your time to write blog posts about how you put the power in the hands of the user.

Simple required field validation using jQuery

Sometimes, you work within the constraints of an existing application, trying to change as little as possible. So when a client had a very simple (two field) form, that showed up as a Bootstrap modal, and I needed to make sure the user didn’t click the submit button until they filled out all the fields, I simply dropped in the following JavaScript.

$(document).on('change keyup', '[required]', function() {
		var targetForm = $($('[required]')[0].form);
		var requiredOk = targetForm.find('[required]').filter(function () { return $(this).val().length == 0; }).length == 0;
		targetForm.find(':submit').prop('disabled', !requiredOk);
});

I then marked up my form fields by adding required property to each field that was required to be true. A simplified example follows.

<form id="form1">
  <input type="text" id="input1" required /><br />
  <textarea id="textarea1" required></textarea><br />
  <button type="submit" disabled>
  Submit
  </button>
</form>

Now, your submit buttons will automatically disable/enable. It doesn’t work in more complex forms with other field types, but I did start off by saying this was a “simple” solution, after all.

JSFiddle here: https://jsfiddle.net/05wyu3az/

Specifying time zone in SQL DATETIME DEFAULTs

You’ve had your existing application running for years, relying on DEFAULT constraints to set DATETIME fields to the current date using GETDATE(). Everything was great until you migrated to Azure SQL Database – and realized that your SQL Server is now set to UTC, and you can’t change it.

Fortunately, you can change your DEFAULT GETDATE() constraint to save date in your time zone. Just specify the default as shown below, specifying your time zone of choice:

default convert(datetimeoffset, getdate()) at time zone 'Eastern Standard Time'

To prove this, run the following, and note the output will show the time in Eastern Standard Time as opposed to UTC.

create table #test (
     val nvarchar(max),
     dt datetime not null default convert(datetimeoffset, getdate()) at time zone 'Eastern Standard Time'
 )
 insert into #test ( val ) values ('yo')
 select * from #test

Voila! No more time zone issue.