• Turns Out I Have Strong Feelings About Socks

    Socks in shoes

    A little bit about myself: I consider myself to be a minimalist. One of the ways that I make that happen is that I own very few clothes, well, at least I own fewer clothes than most other people.

    I definitely recognize that it is a bit extreme. When my wife and I first started dating, she gave me a look of horror when I told her that I only own two pairs of pants. This experience isn’t, I have been accused of dressing like a cartoon character because I’m always wearing the same garments. Don’t worry, I do own more than two pairs of pants today.

    While all this works well for me, I understand that this is not the right choice for everyone.

    I do; however, have strong feelings about socks. I feel that most people have far too much variety and would be significantly happier if they adhered to some of the principles I’m laying out here.

    1. All of your socks must be the same

    All of your socks should match. They should match in color, style, brand, and fabric. If all the socks match, that means that they can each be paired with one another. If all socks can be paired with all other socks, you never have to worry about sorting them when putting away laundry.

    When doing this, you should pick socks from a higher-end clothing brand. Not because the socks are inherently better, but because they change the socks up less so you have a greater chance of buying replacements later.

    Be wary of brands that label their socks “left” and “right” (Bombas does this). I assume they do this because if you lose one, you’re more likely to buy another pair because they are not interchangeable.

    2. All socks must be black

    Black socks are the most versatile of the socks that you can own. Black socks don’t stain easily. Black socks can be washed with your colors. Black socks also match the greatest number of shoes and clothing options.

    3. Socks should be made with low-friction fibers

    The socks you own should be athletic-oriented and made of synthetic polyester or Merino wool. The benefit here is that you’ll fewer (if any) blister in the future. Properly setting up your socks as well as your shoes to match mean that you can hike long distances and never have to worry about a blister. I have recommended this choice to multiple friends, and all of them have said that it has improved or eliminated their foot issues.

    4. The socks should be athletic-oriented

    As a lazy person, I need to reduce as many barriers as possible to exercise. This means that I have to be able to work out in my socks. This does increase the wear the socks because they’re doing double duty; however, since all socks are interchangeable, replacement is easier.

    Another advantage of athletic socks is they tend to have more padding. For me, I need all the padding I can get.

    Isn’t this a bit dogmatic?

    When first starting out with this, it’s a good idea to set firm boundaries. Once you start mixing in different sock brands, it’s very easy to end back up at square one.

    I’m also not saying that you can NEVER have different socks, but those socks are the exceptional ones and not the ones for daily use. For example, I have two pairs of socks that I’m going to hold on to for a while: the socks I wore on my wedding day and the socks with my daughter’s face printed on them.

    If you live in a climate where temperature is quite variable (Wisconsin for instance), it may be worth having a warm and cold set of socks. I’ve needed thicker socks to deal with the colder midwest weather.

    How should I go about doing this?

    Since getting a whole drawer of nice athletic socks can be pretty big commitment (not to mention the expense), I recommend going to the store and picking out a few different options as single pairs. Wear them for a little while before taking the plunge. Why not date the sock before you marry it?

    Depending on the brand, you may get lucky and could find some bulk pricing at purchase. Unlikely for higher-end socks, but it’s worth a try.

    Do you have any sock recommendations?

    This is highly personal, but I’m a big fan of Balega. They last awhile (years for me) and aren’t too hard to get ahold of.

    In the future, I might experiment with Darn Tough. I like the idea of having a sock with a lifetime warranty. I’m not sure if this would be abusing the policy, but if I’m reading this correctly, this may be the last set of socks I’d have to buy.

    In short, this is definitely worth a try. Give yourself the gift of spending less time sorting laundry and making fewer decisions. It’s well worth the upfront effort.

  • Migrating Database on App Startup with Entity Framework

    Entity Framework migrations are a great way to keep your database schema in sync with your data model. For local and development environments, automatically running migrations at startup can simplify your workflow significantly.

    Implementation with Environment Guards

    Let’s look at properly guarded migration code for different application types.

    Implementing this is ASP.NET

    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
    
    var app = builder.Build();
    
    // Only migrate in development environment
    if (app.Environment.IsDevelopment())
    {
        using (var scope = app.Services.CreateScope())
        {
            var services = scope.ServiceProvider;
            try
            {
                var context = services.GetRequiredService<ApplicationDbContext>();
                context.Database.Migrate();
                Console.WriteLine("Database migrations applied successfully");
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "Error applying database migrations");
            }
        }
    }
    
    app.Run();

    Production Warning

    For production environments, never automatically apply migrations at startup. Instead:

    • Run migrations as a separate, controlled deployment step
    • Use database deployment tools or migration scripts
    • Ensure proper backups before schema changes

    Remember, this approach is intended only for local development and testing environments where database consistency is prioritized over startup performance and control.

  • Setting Window Size With Caliburn Micro

    This is something that has actually bugged me for a while. Once I figured it out, it annoyed me that I didn’t figure it out sooner.

    When displaying a window in Caliburn.Micro, you can set attributes about the Window object when calling it.

    So, let’s say you want to set the height and width on the window to 600 x 300:

    First, you would start with something like this:

    public class ShellViewModel : PropertyChangedBase, IShell
    {
        private readonly IWindowManager windowManager;
    
        public ShellViewModel()
        {
            this.windowManager = new WindowManager();
            this.windowManager.ShowWindow(new LameViewModel());
        }
    }

    There are two other fields on the ShowWindow method. The third parameter lets you dynamically set the attributes on the Window object.

    public class ShellViewModel : PropertyChangedBase, IShell
    {
        private readonly IWindowManager windowManager;
    
        public ShellViewModel()
        {
            this.windowManager = new WindowManager();
    
            dynamic settings = new ExpandoObject();
            settings.Height = 600;
            settings.Width = 300;
    
            this.windowManager.ShowWindow(new LameViewModel(), null, settings);
        }
    }

    I wish there was more information about working with this on the documentation, but there you have it.

  • Wiring Up Fluent Validation With WPF

    Update

    I originally wrote this proof of concept about 7 years ago early in my time with WPF. I published it on an earlier iteration of my blog via Github Gist and Wordpress (You can find the gist here). After which point I pretty much forgot out it.

    I recently came across the Gist and found that it has helped out a surprising number of people. So, it makes sense to me to pull this in to a blog and annotate it a bit better. If there is interest, I may make some more changes to streamline to examples and take advantage of newer C# features.

    Overview

    Fluent Validation is my favorite validation library for C#. It is pretty straightforward to use and it forces you to separate out the validation code into a separate class which IMHO generally makes the code cleaner.

    The library includes some pretty standard integrations with ASP.NET, but there never was a first class implementation that integrates a validator with a WPF view model. This post is a proof of concept that I put together to bridge that gap.

    Building the Validator

    For the purposes of demonstration. We’ll have a UserViewModel which has a property for Name, E-Mail, and Zip Code. So we’ll write a quick validator for the usual aspects of that data.

    using System.Text.RegularExpressions;
    using FluentValidation;
    using WpfFluentValidationExample.ViewModels;
    
    namespace WpfFluentValidationExample.Lib
    {
        public class UserValidator : AbstractValidator<UserViewModel>
        {
            public UserValidator()
            {
                RuleFor(user => user.Name)
                    .NotEmpty()
                    .WithMessage("Please Specify a Name.");
    
                RuleFor(user => user.Email)
                    .EmailAddress()
                    .WithMessage("Please Specify a Valid E-Mail Address");
    
                RuleFor(user => user.Zip)
                    .Must(BeAValidZip)
                    .WithMessage("Please Enter a Valid Zip Code");
            }
    
            private static bool BeAValidZip(string zip)
            {
                if (!string.IsNullOrEmpty(zip))
                {
                    var regex = new Regex(@"\d{5}");
                    return regex.IsMatch(zip);
                }
                return false;
            }
        }
    }

    Creating the View to Present the Validation

    Here is the view that I created to demonstrate this implementation.

    This is a standard form written in XAML with a few differences:

    • In the property binding for the text on the textboxes we can see that I added a “ValidatesOnDataErrors=True” clause.
    • On each one of those textboxes as well, I expanded out the Validation.Error template property with a stack panel which will slot in a validation error when one occurs.
    <Window
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:viewModels="clr-namespace:WpfFluentValidationExample.ViewModels" x:Class="WpfFluentValidationExample.Views.UserView"
            Title="UserView" Height="300" MinWidth="500">
        <Window.DataContext>
            <viewModels:UserViewModel/>
        </Window.DataContext>
        <StackPanel>
            <StackPanel Orientation="Horizontal">
                <Label Content="Name" Margin="10"/>
                <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="200" Margin="10">
                    <Validation.ErrorTemplate>
                        <ControlTemplate>
                            <StackPanel Orientation="Horizontal">
                                <AdornedElementPlaceholder x:Name="textBox"/>
                                <TextBlock Margin="10" Text="{Binding [0].ErrorContent}" Foreground="Red"/>
                            </StackPanel>
                        </ControlTemplate>
                    </Validation.ErrorTemplate>
                </TextBox>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Label Content="E-Mail" Margin="10"/>
                <TextBox Text="{Binding Email, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="200" Margin="10">
                    <Validation.ErrorTemplate>
                        <ControlTemplate>
                            <StackPanel Orientation="Horizontal">
                                <AdornedElementPlaceholder x:Name="textBox"/>
                                <TextBlock Margin="10" Text="{Binding [0].ErrorContent}" Foreground="Red"/>
                            </StackPanel>
                        </ControlTemplate>
                    </Validation.ErrorTemplate>
                </TextBox>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Label Content="Zip" Margin="10"/>
                <TextBox Text="{Binding Zip, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="200" Margin="10">
                    <Validation.ErrorTemplate>
                        <ControlTemplate>
                            <StackPanel Orientation="Horizontal">
                                <!-- Placeholder for the TextBox itself -->
                                <AdornedElementPlaceholder x:Name="textBox"/>
                                <TextBlock Margin="10" Text="{Binding [0].ErrorContent}" Foreground="Red"/>
                            </StackPanel>
                        </ControlTemplate>
                    </Validation.ErrorTemplate>
                </TextBox>
            </StackPanel>
            <Button Margin="10">Submit</Button>
        </StackPanel>
    </Window>

    The Code Behind on the View

    Including this for the sake of completeness. Please ignore.

    using System.Windows;
    using WpfFluentValidationExample.ViewModels;
    
    namespace WpfFluentValidationExample.Views
    {
        /// <summary>
        /// Interaction logic for UserView.xaml
        /// </summary>
        public partial class UserView : Window
        {
            public UserView()
            {
                InitializeComponent();
                DataContext = new UserViewModel();
            }
        }
    }

    The Viewmodel

    The viewmodel is the integration point between the fluent validator, the data, and the view. Initially this looks like a standard viewmodel. We have a property for the name, email, and zip code.

    Closer to the bottom, you’ll find the integration for the validation:

    • There is the integration of the overloaded [] operator which matches up the property name with the validation.
    • There is also the addition of an Error string which combines together all the errors into a single string
    using System;
    using System.ComponentModel;
    using System.Linq;
    using WpfFluentValidationExample.Lib;
    
    namespace WpfFluentValidationExample.ViewModels
    {
        public class UserViewModel : INotifyPropertyChanged, IDataErrorInfo
        {
            private readonly UserValidator _userValidator = new UserValidator();
    
            private string _zip;
            private string _email;
            private string _name;
    
            public string Name
            {
                get { return _name; }
                set
                {
                    _name = value;
                    OnPropertyChanged("Name");
                }
            }
    
            public string Email
            {
                get { return _email; }
                set
                {
                    _email = value;
                    OnPropertyChanged("Email");
                }
            }
    
            public string Zip
            {
                get { return _zip; }
                set
                {
                    _zip = value;
                    OnPropertyChanged("Zip");
                }
            }
    
            public string this[string columnName]
            {
                get
                {
                    var firstOrDefault = _userValidator.Validate(this).Errors.FirstOrDefault(p => p.PropertyName == columnName);
                    if (firstOrDefault != null)
                        return _userValidator != null ? firstOrDefault.ErrorMessage : "";
                    return "";
                }
            }
    
            public string Error
            {
                get
                {
                    if (_userValidator != null)
                    {
                        var results = _userValidator.Validate(this);
                        if (results != null && results.Errors.Any())
                        {
                            var errors = string.Join(Environment.NewLine, results.Errors.Select(x => x.ErrorMessage).ToArray());
                            return errors;
                        }
                    }
                    return string.Empty;
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged(string propertyName)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    Conclusion

    Thanks for sticking around to the end. I hope this helped you out improving the validation on your WPF application.

    If this helped you out, it would be helpful to star the Github Gist that is linked at the top of the post so that I know that I’m helping people out.

  • Getting Started With Hangfire

    Putting together this guide for testing out hangfire to fill in some of the gaps I found through creating a greenfield hangfire application.

    Setting up a database to test with

    Since I don’t have a SQL Server running that I can just attach to. I’ll have to run through the initial set up of SQL Express Edition

    1. Navigate to the website: https://www.microsoft.com/en-us/sql-server/sql-server-downloads
    2. Under the “Express” item at the bottom of the fold, hit the button “Download Now >”
    3. Run the installer
    4. Select “Basic” option
    5. Hit the “Accept” button without reading anything because I actually want to get some work done.
    6. Install it to the default directory

    Creating the Blank Project to Get Started

    1. Open up Visual Studio 2019
    2. Choose “Create a new Project”
    3. Search for asp.net project type. Since I would like to use the dashboard for this, I will have to use at least a .net core project. So, I selected the ASP.NET Core Empty project
    4. I named the application HangfireExperimentation
    5. Selected the Target Framework to be .NET 5.0 (Current).

    Setting up the Test Database for Hangfire to Use

    1. On the top menu go to View > SQL Server Object Explorer. This will open up a tab that allows you to navigate through the local database.
    2. From this view: Expand out SQL Server > {You’re localdb instance} > Databases
    3. Right Click on Databases and select “Add New Database”
    4. Name that Database HangfireExperimentation
    5. Expand out that database
    6. Right click on the database that was just created and select “Properties”
    7. In the properties tab, copy the connection string and save it somewhere to use later. Mine was something like:
    Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=HangfireExperimentation;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False

    Set up the Nuget Packages

    1. In the solution explorer, right click on the project and select “Manage nuget Packages…”
    2. Click on the “Browse” tab in the window
    3. Search for “Hangfire”
    4. Install the following packages
      • Hangfire.Core
      • Hangfire.SqlServer
      • Hangfire.AspNetCore

    Updating the appsettings.json file

    1. Open up the appsettings.json file
    2. Update the LogLevel to include hangfire
    3. Add the connection string that you saved earlier to the list of connection strings

    My appsettings.json file ended up looking like this:

    {
    "Logging": {
        "LogLevel": {
        "Default": "Information",
        "Microsoft": "Warning",
        "Microsoft.Hosting.Lifetime": "Information",
        "Hangfire": "Information"
        }
    },
    "ConnectionStrings": {
        "HangfireConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=HangfireExperimentation;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
    },
    "AllowedHosts": "*"
    }

    Update the Startup.cs file

    1. Update the ConfigureServices() method so that it looks like this:
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHangfire(configuration => configuration
            .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
            .UseSimpleAssemblyNameTypeSerializer()
            .UseRecommendedSerializerSettings()
            .UseSqlServerStorage(ConfigurationManager.ConnectionStrings["HangfireConnection"].ConnectionString, new SqlServerStorageOptions
            {
                CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
                QueuePollInterval = TimeSpan.Zero,
                UseRecommendedIsolationLevel = true,
                DisableGlobalLocks = true
            }));
    
        services.AddHangfireServer();
    
        services.AddMvc(option => option.EnableEndpointRouting = false);
    }
    1. Run through and add the missing references

    2. Add in the Configuration field that ConfigureServices(…) uses to get the connection string

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }
    
    public IConfiguration Configuration { get; }
    1. Update the Configure(…) method to look like this:
    public void Configure(IApplicationBuilder app, 
        IWebHostEnvironment env, 
        IBackgroundJobClient backgroundJobs)
    {
        app.UseStaticFiles();
    
        app.UseHangfireDashboard();
        backgroundJobs.Enqueue(() => Console.WriteLine("Hello world from Hangfire!"));
    
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }

    Testing things out

    1. Hit play on Visual Studio
    2. Since this was created with blank project
    3. Navigate to the hangfire dashboard. My url was: https://localhost:44321/hangfire
    4. PROFIT?!!