Addicted to Eating

Hey all…still here and still thinking about things to post.  I have code experiments on the back burner waiting for work to get a little less busy so I can polish up the examples…but the bulk of my spare time goes towards working out these days.

I don’t mean I’m spending hours on end working out (well…maybe on grappling days)…but the whole process of working out really takes any extra energy out of me that I previously was using to do my coding posts…so, I’ll get around to them…but I’m trying to keep in the habit of posting something at least once a week…so this week it’ll be about my struggle with eating.

I can tell you without hesitation…of all the addictive things I’ve done in my lifetime, nothing compares to the pull eating has over me.  Time and again it has won the battle for dominance over my body…as I’m sure one day it will again defeat my resolve and determination.  If I don’t constantly watch what I’m doing I can and will mindlessly eat my way through any amount of food…entire family sized pizza’s…bags of chips…tubs of ice cream…all have fallen victim to my binges.

I’m a good streak right now.  I have good support to lean on in weak moments.  I have a powerful goal that helps keep me focused.  Even through all that the demon claws at me begging me to fill my face with endless calories.  But November is only 6-7 months away and my goal of getting to below 300 before competing is within my reach…actually getting below 280, which is what I weighed when I graduated high school, is within my reach.  That would be very weird to be in better shape at 43 than I was at 18…a good weird.

As long time readers will know I attribute most of my current weight loss to my working out.  I put a lot of effort into it.  I burn through a ton of calories.  I’m very serious about getting something out of it everytime I put effort into working out, because I HATE HATE HATE doing it…so I better get something out of it for my trouble.

What I had been doing is just eating normally while working out like crazy…and that worked for a while.  I started to stall in my weight loss though so I then cut out eating fast food.  That made a huge difference.  Apprently that stuff is bad for you, who knew.

Well…that only worked for so long as well and I began to stall once again…so now I’ve added cutting back the amount I’m eating into the whole equation…and I’ve again started to drop pounds…but the eating addiction has started rearing it’s ugly head.  Hungry or not I often think of snacking.  😦

It’s taking a lot more determination than I thought it would to resist eating…it’s even harder to do then not listen to MJK music.  Every extra second I have my mind drifts to thinking about stuffing something in my face.  Ugh…will I ever be rid of this addiction.  My whole life it’s been a monkey on my back.  I’d seriously consider having the procedure done if they knew a place in my brain to zap with a sodering iron or something to kill the eating addiction section of the brain…hmmm, maybe that’s how zombies are made.  😉

Anyway, enough complaining for now…as of the moment I’m winning the eatting addiction battle and I’m at 304 pounds (starting from an estimated 360).  That’s victory enough for today.  Hope this has inspired someone to hold the demon at bay!

Advertisements
Addicted to Eating

I am a MACHINE!

No intro…getting right into the post…oh crap, I guess that was an intro just now…  😛

So I’ve been told and have read things over the years about how to stay in shape.  I’m sure you all know it boils down to exercising and eating right.  If you don’t…let me save you some time…IT ALL BOILS DOWN TO EXERCISING AND EATING RIGHT.  There, now you know the secret.

Now that I’m drinking the “get in shape” coolaid so to speak, I can notice that my body is responding.  I’ve been feeling stronger…my stamina is getting better…I’m loosing weight…so I’d guess that means my body is fixing the damage.  However I still look old.

AFAIK, when you’re working out…you are actually causing damage to your muscles which is what causes them to grow back stronger and more resistent to being damaged again…which means that your body is generating new tissue and cells.

Aging is the process of your cells degrading over time as they’re being replaced…kinda like a copy of a copy of a copy of a cpy of a cpy of a cpy of a cp of a cp of a cp (see what I did there?)

One of my questions is if you body is creating new cells for your muscles…those are brand new…shouldn’t they be starting out at the “original” point instead of the “copy of a copy of a copy” point?

And that just leads me to another question about skin…as I’m loosing weight I can see more and more flab, which is skin that’s been stretched out too far and won’t shrink…what would happen if I just had all the flab cut off and let the skin grow back on it’s own (if that was even possible without dying).  Would it grow back as the stretched skin or would it came back as the “original”?

I’ve always have had the point of view that my body is just a machine…and it reflects how well I’ve taken care of it.  I would say it’s just like a car in that if you put crappy fuel in and never get it serviced…the car would die before it’s time…however in that analogy if you start putting in good fuel and servicing the car…it doesn’t magically heal that car from all wounds…the damage is done.  From what I’ve noticed in myself, that’s not entirely so for people when they let themselves go and start getting back into good health.  The damage may be done…but we heal that damage to a degree, but you just cannot heal the damage that aging does.  It’s like there some master blueprint of what it takes to make the components in your body and it’s stored somewhere out in the open where the blueprint continuously degrades from being eroded by weather…and there’s nothing that can be done about it.  Man that pisses me off!  The idea that I’m just going to keep withering away no matter how much time and effort I put into stopping it…it’s very frustrating.

But then I think of the inevitable zombie apocolypse that’s sure to take place any day now and I know I’ll be put all this working out and getting into shape to good use.  😉

Anyway…just some food for thought.  Feel free to comment or not.  Enjoy.  🙂

I am a MACHINE!

WPF using BackgroundWorker vs using Task

Today I’m expanding on the ExampleAppinator post that I made last week which will be the foundation to my examples going forward.  In this I’m going to show an example that simply outputs the contents of the KitchenSink tables into grids…1 set using BackgroundWorker to create threads for doing the fetching of data, the other set is using a Task.

I’ll post the ending source code to the WPF example app at the end of this post.

The first thing I noticed when I put this together is that when there wasn’t a lot of data in the table, it really took little to no time to ever get anything out of the database so it was nealry impossible to see any difference in BackgroundWorker vs Task…which is what lead to me creating KitchenSinkClogger in the ExampleAppinator post.  I created that and let it run for the weekend…it filled up my database with about 250000 records.  Perfect, now I have some meat to sink my teeth into.

Now when I launched the test WPF app, I ran into some real perf problems.  At this stage of the test I had 12 DataGrid’s being loaded up simultaniously, 6 using BackgroundWorker threads, 6 using Task threads.  When I launched it, for about 5 solid minutes nothing happened but churning in the debugger.  I paused it in the debugger a couple times just to verify that there were worker threads churning in the background…and as far as I can tell, none of them should have been on the UI thread so the UI should have never locked up…I’m a bit confused by that one.

From here I had a thought that maybe the app was becoming unresponsive because I was trying to load up 12 DataGrid’s with 250000 records each and it was a memory issues…so I decided to add pagers to my DataGrids…and since that’s a total pain in the backside with normal built in WPF controls I decided to switch over to using Telerik controls at this point since they have a ready made RadDataPager object I could use.  About 10 minutes later…everything was switched over to RadBusyIndicator’s and RadGridView’s with a RadDataPager in place and as I tested out each individual grid they were all working smooth…until…I turned them all on to load at the same time again.  Again the app was brought down to it’s knees in perf.

So this is where I’m at right now as of 5/11/2016.  I think my next move is to make a post on the Telerik forums now that I’m using their controls and see if I get lucky with an answer on how to make my little test more performant…but in all honesty I don’t think it has anything to do with the controls.  I think it has something to do with the threads…and that I’m obviously attaching to the UI thread somewhere.

Update on 5/17/2016 – I got a reply from Telerik and they weren’t able to help figure out where I’m getting the performance hit…next I’m going to try disabling all the grids but one and see what kind of perf I get there…then start adding them back in one at a time…all the while monitoring threads to see if I can notice anything strange going on.

As always I encourage anyone reading this to leave a comment if you have questions or feedback…I know I don’t know what I’m doing…but I’m totally willing to learn new techniques.  🙂

Update on 5/25/2016 – Yes I know I haven’t posted in about a week…I’ve gotten busy at work again which cuts into my post preparation time so :P.  Quality posts take time to put together…and since mine suck it takes less time…but still a lot of time.

Update on 5/31/2016 – I did some work on this today and started by narrowing down the grids being returned from the database to just one.  I had to kill my database with over 1 million records in it and I’m currently rebuilding it…but at the moment I get about 11k records returned so fast that I can’t even see the Busy Indicator come up…so now I’m going to wait for the database to fill up again so I can see that.  Trying to make sure that each piece is working individually.  Once I can prove that the busy indicator comes up for a BackgroundWorker thread…then I’m going to try returning the same set of information using a Task thread to see if there’s any noticible difference.  Anyway…that’s where I’m at with this for now.  Stay tuned…I’ll get this figured out eventually.

Update on 6/1/2016 – So I got 500k records in my database now and tried the grid using the BackgroundWorker thread again.  Low and behold the Busy Indicator shows up…woohoo.  Now onto seeing if I get the same results using a Task instead.

Update on 6/7/2016 – Today I made some great progress.  I learned what’s really hanging up the BW thread and the Task threads both is that I’m creating a whole bunch of them that are all using the same resources…the network…so there’s a ton of waiting and locking and syncronizing that the OS is doing with the threads and it causes the UI to seem like it’s getting hung up because the system is busy doing all this work sync’ing background threads.  What I learned to do is put a lock on the parent DLL object I have for all my Linq classes and models with each BW thread that I create.  This way I’m managing the sync’ing by forcing all the threads to wait until usage of the parent object free’s up.  The UI displays the Busy Indicator…all the worker threads spin up just fine…and the UI stays responsive.  The 1 down side of this that I noticed is now you have to wait for each table to load once at a time…even though the busy indicator is there and gives a good experience to the user, I feel there could be some more done here to improve the data retrieval experience.  Also I couldn’t retrieve a table with 5 million records.  Got an OOM expcetion thrown from Linq.  Stupid Linq.  😛  So I’m off to learn some data virtualization techniques so I can apply them to my example app and see if I can actually get back to my original theme to this post…which is better?  BW or Task?  Man, this post is crossing lots of different disciplines.  I like it!  Stay tuned!!!  🙂  I’ll be posting an update to the source code once I get the data virtualization part figured out…

MainWindow XAML:

<Window Height="350"
        mc:Ignorable="d"
        Title="MainWindow"
        Width="525"
        x:Class="CSharpWPFAppinator.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:CSharpWPFAppinator"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
    <Grid>
        <TabControl x:Name="KitchenSinkTabControl">
            <TabItem    Header="BW - Engine"
                        x:Name="BWEngineTab" />
            <TabItem    Header="BW - History"
                        x:Name="BWHistoryTab" />
            <TabItem    Header="BW - Lifeform"
                        x:Name="BWLifeformTab" />
            <TabItem    Header="BW - Planet"
                        x:Name="BWPlanetTab" />
            <TabItem    Header="BW - Species"
                        x:Name="BWSpeciesTab" />
            <TabItem    Header="BW - Vehicle"
                        x:Name="BWVehicleTab" />
            <TabItem    Header="Task - Engine"
                        x:Name="TaskEngineTab" />
            <TabItem    Header="Task - History"
                        x:Name="TaskHistoryTab" />
            <TabItem    Header="Task - Lifeform"
                        x:Name="TaskLifeformTab" />
            <TabItem    Header="Task - Planet"
                        x:Name="TaskPlanetTab" />
            <TabItem    Header="Task - Species"
                        x:Name="TaskSpeciesTab" />
            <TabItem    Header="Task - Vehicle"
                        x:Name="TaskVehicleTab" />
        </TabControl>
    </Grid>
</Window>

MainWindow CS Code Behind:

using System.Windows;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            KitchenSinkDLL.KitchenSinkDLL.ConnectionString = @"Data Source=cmd-roswell;Initial Catalog=KitchenSink;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

            foreach(TabItem item in KitchenSinkTabControl.Items)
            {
                switch(item.Name)
                {
                    case "BWEngineTab":
                        BWEngineUserControl bwEngineControl = new BWEngineUserControl();
                        bwEngineControl.RefreshData();
                        item.Content = bwEngineControl;
                        break;
                    case "BWHistoryTab":
                        BWHistoryUserControl bwHistoryControl = new BWHistoryUserControl();
                        bwHistoryControl.RefreshData();
                        item.Content = bwHistoryControl;
                        break;
                    case "BWLifeformTab":
                        BWLifeformUserControl bwLifeformControl = new BWLifeformUserControl();
                        bwLifeformControl.RefreshData();
                        item.Content = bwLifeformControl;
                        break;
                    case "BWPlanetTab":
                        BWPlanetUserControl bwPlanetControl = new BWPlanetUserControl();
                        bwPlanetControl.RefreshData();
                        item.Content = bwPlanetControl;
                        break;
                    case "BWSpeciesTab":
                        BWSpeciesUserControl bwSpeciesControl = new BWSpeciesUserControl();
                        bwSpeciesControl.RefreshData();
                        item.Content = bwSpeciesControl;
                        break;
                    case "BWVehicleTab":
                        BWVehicleUserControl bwVehicleControl = new BWVehicleUserControl();
                        bwVehicleControl.RefreshData();
                        item.Content = bwVehicleControl;
                        break;
                    case "TaskEngineTab":
                        TaskEngineUserControl taskEngineControl = new TaskEngineUserControl();
                        taskEngineControl.RefreshData();
                        item.Content = taskEngineControl;
                        break;
                    case "TaskHistoryTab":
                        TaskHistoryUserControl taskHistoryControl = new TaskHistoryUserControl();
                        taskHistoryControl.RefreshData();
                        item.Content = taskHistoryControl;
                        break;
                    case "TaskLifeformTab":
                        TaskLifeformUserControl taskLifeformControl = new TaskLifeformUserControl();
                        taskLifeformControl.RefreshData();
                        item.Content = taskLifeformControl;
                        break;
                    case "TaskPlanetTab":
                        TaskPlanetUserControl taskPlanetControl = new TaskPlanetUserControl();
                        taskPlanetControl.RefreshData();
                        item.Content = taskPlanetControl;
                        break;
                    case "TaskSpeciesTab":
                        TaskSpeciesUserControl taskSpeciesControl = new TaskSpeciesUserControl();
                        taskSpeciesControl.RefreshData();
                        item.Content = taskSpeciesControl;
                        break;
                    case "TaskVehicleTab":
                        TaskVehicleUserControl taskVehicleControl = new TaskVehicleUserControl();
                        taskVehicleControl.RefreshData();
                        item.Content = taskVehicleControl;
                        break;
                    default:
                        break;
                }
            }
        }
    }
}

BackgroundWorkerUserControl:

using KitchenSinkDLL.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using Telerik.Windows.Controls;
using Xceed.Wpf.Toolkit;

namespace CSharpWPFAppinator
{
    public class BackgroundWorkerUserControl<T> : UserControl
    {
        public BackgroundWorkerUserControl()
        {
            BackgroundWorkerGrid = new Grid();
            BackgroundWorkerGrid.ColumnDefinitions.Add(new ColumnDefinition());
            BackgroundWorkerGrid.RowDefinitions.Add(new RowDefinition());
            BackgroundWorkerGrid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });

            BackgroundWorkerRadGridView = new RadGridView();
            Grid.SetColumn(BackgroundWorkerRadGridView, 0);
            Grid.SetRow(BackgroundWorkerRadGridView, 0);
            BackgroundWorkerGrid.Children.Add(BackgroundWorkerRadGridView);

            BackgroundWorkerRadDataPager = new RadDataPager();
            BackgroundWorkerRadDataPager.AutoEllipsisMode = AutoEllipsisModes.Both;
            BackgroundWorkerRadDataPager.DisplayMode = PagerDisplayModes.All;
            BackgroundWorkerRadDataPager.NumericButtonCount = 10;
            BackgroundWorkerRadDataPager.PageSize = 40;
            BackgroundWorkerRadDataPager.VerticalAlignment = VerticalAlignment.Bottom;
            BackgroundWorkerRadDataPager.Source = BackgroundWorkerRadGridView.Items;
            Grid.SetColumn(BackgroundWorkerRadDataPager, 0);
            Grid.SetRow(BackgroundWorkerRadDataPager, 1);
            BackgroundWorkerGrid.Children.Add(BackgroundWorkerRadDataPager);

            BackgroundWorkerRadBusyIndicator = new RadBusyIndicator();
            BackgroundWorkerRadBusyIndicator.Content = BackgroundWorkerGrid;

            Content = BackgroundWorkerRadBusyIndicator;

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            BackgroundWorker = new BackgroundWorker();
            BackgroundWorker.DoWork += BackgroundWorkerDoWork;
            BackgroundWorker.RunWorkerCompleted += BackgroundWorkerCompleted;
        }

        private BackgroundWorker BackgroundWorker { get; set; }

        private Grid BackgroundWorkerGrid { get; set; }

        private RadBusyIndicator BackgroundWorkerRadBusyIndicator { get; set; }

        private RadDataPager BackgroundWorkerRadDataPager { get; set; }

        private RadGridView BackgroundWorkerRadGridView { get; set; }

        private DateTime LastHistoryUpdate { get; set; }

        private void BackgroundWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Dispatcher.BeginInvoke(new Action<IEnumerable<T>>(RefreshBackgroundWorkerDataGrid), e.Result);
        }

        private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = KitchenSinkDLL.KitchenSinkDLL.ReadTable<T>();
        }

        private void RefreshBackgroundWorkerDataGrid(IEnumerable<T> engines)
        {
            BackgroundWorkerRadGridView.ItemsSource = null;

            if (engines != null)
            {
                LastHistoryUpdate = HistoryModel.ReadLastHistoryUpdate();
                BackgroundWorkerRadGridView.ItemsSource = engines;
            }

            BackgroundWorkerRadBusyIndicator.IsBusy = false;
        }

        public void RefreshData()
        {
            if (!BackgroundWorker.IsBusy)
            {
                DateTime currentLastHistoryUpdate = HistoryModel.ReadLastHistoryUpdate();

                if (currentLastHistoryUpdate > LastHistoryUpdate)
                {
                    BackgroundWorkerRadBusyIndicator.IsBusy = true;
                    BackgroundWorker.RunWorkerAsync();
                }
            }
        }
    }
}

TaskUserControl:

using KitchenSinkDLL.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Telerik.Windows.Controls;
using Xceed.Wpf.Toolkit;

namespace CSharpWPFAppinator
{
    public class TaskUserControl<T> : UserControl
    {
        public TaskUserControl()
        {
            TaskGrid = new Grid();
            TaskGrid.ColumnDefinitions.Add(new ColumnDefinition());
            TaskGrid.RowDefinitions.Add(new RowDefinition());
            TaskGrid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });

            TaskRadGridView = new RadGridView();
            Grid.SetColumn(TaskRadGridView, 0);
            Grid.SetRow(TaskRadGridView, 0);
            TaskGrid.Children.Add(TaskRadGridView);

            TaskRadDataPager = new RadDataPager();
            TaskRadDataPager.AutoEllipsisMode = AutoEllipsisModes.Both;
            TaskRadDataPager.DisplayMode = PagerDisplayModes.All;
            TaskRadDataPager.NumericButtonCount = 10;
            TaskRadDataPager.PageSize = 40;
            TaskRadDataPager.VerticalAlignment = VerticalAlignment.Bottom;
            TaskRadDataPager.Source = TaskRadGridView.Items;
            Grid.SetColumn(TaskRadDataPager, 0);
            Grid.SetRow(TaskRadDataPager, 1);
            TaskGrid.Children.Add(TaskRadDataPager);

            TaskRadBusyIndicator = new RadBusyIndicator();
            TaskRadBusyIndicator.Content = TaskGrid;

            Content = TaskRadBusyIndicator;

            if (DesignerProperties.GetIsInDesignMode(this))
                return;
        }

        private Grid TaskGrid { get; set; }

        private RadBusyIndicator TaskRadBusyIndicator { get; set; }

        private RadDataPager TaskRadDataPager { get; set; }

        private RadGridView TaskRadGridView { get; set; }

        private DateTime LastHistoryUpdate { get; set; }

        private IEnumerable<T> Records { get; set; }

        private void FetchDataForControl()
        {
            Records = KitchenSinkDLL.KitchenSinkDLL.ReadTable<T>();
        }

        private void RefreshTaskDataGrid()
        {
            TaskRadGridView.ItemsSource = null;

            if (Records != null)
            {
                LastHistoryUpdate = HistoryModel.ReadLastHistoryUpdate();
                TaskRadGridView.ItemsSource = Records;
            }
        }

        public async Task RefreshData()
        {
            TaskRadBusyIndicator.IsBusy = true;

            await Task.Run(() =>
            {
                DateTime currentLastHistoryUpdate = HistoryModel.ReadLastHistoryUpdate();

                if (currentLastHistoryUpdate > LastHistoryUpdate)
                {
                    FetchDataForControl();
                }
            });

            RefreshTaskDataGrid();
            TaskRadBusyIndicator.IsBusy = false;
        }
    }
}

BWEngineUserControl XAML:

<UserControl    d:DesignHeight="300"
                d:DesignWidth="300"
                mc:Ignorable="d"
                x:Class="CSharpWPFAppinator.BWEngineUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:local="clr-namespace:CSharpWPFAppinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
    <Grid x:Name="BWEngineGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

BWEngineUserControl CS Code Behind:

using KitchenSinkDLL.Models;
using System.ComponentModel;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for BWEngineUserControl.xaml
    /// </summary>
    public partial class BWEngineUserControl : UserControl
    {
        public BWEngineUserControl()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            EngineUserControl = new BackgroundWorkerUserControl<EngineModel>();
            Grid.SetColumn(EngineUserControl, 0);
            Grid.SetRow(EngineUserControl, 0);
            BWEngineGrid.Children.Add(EngineUserControl);
        }

        private BackgroundWorkerUserControl<EngineModel> EngineUserControl { get; set; }

        public void RefreshData()
        {
            EngineUserControl.RefreshData();
        }
    }
}

TaskEngineUserControl XAML:

<UserControl    d:DesignHeight="300"
                d:DesignWidth="300"
                mc:Ignorable="d"
                x:Class="CSharpWPFAppinator.TaskEngineUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:local="clr-namespace:CSharpWPFAppinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
    <Grid x:Name="TaskEngineGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

TaskEngineUserControl CS Code Behind:

using KitchenSinkDLL.Models;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for TaskEngineUserControl.xaml
    /// </summary>
    public partial class TaskEngineUserControl : UserControl
    {
        public TaskEngineUserControl()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            EngineUserControl = new TaskUserControl<EngineModel>();
            Grid.SetColumn(EngineUserControl, 0);
            Grid.SetRow(EngineUserControl, 0);
            TaskEngineGrid.Children.Add(EngineUserControl);
        }

        private TaskUserControl<EngineModel> EngineUserControl { get; set; }

        public async Task RefreshData()
        {
            await EngineUserControl.RefreshData();
        }
    }
}

BWHistoryUserControl XAML:

<UserControl    d:DesignHeight="300"
                d:DesignWidth="300"
                mc:Ignorable="d"
                x:Class="CSharpWPFAppinator.BWHistoryUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:local="clr-namespace:CSharpWPFAppinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
    <Grid x:Name="BWHistoryGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

BWHistoryUserControl CS Code Behind:

using KitchenSinkDLL.Models;
using System.ComponentModel;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for BWHistoryUserControl.xaml
    /// </summary>
    public partial class BWHistoryUserControl : UserControl
    {
        public BWHistoryUserControl()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            HistoryUserControl = new BackgroundWorkerUserControl<HistoryModel>();
            Grid.SetColumn(HistoryUserControl, 0);
            Grid.SetRow(HistoryUserControl, 0);
            BWHistoryGrid.Children.Add(HistoryUserControl);
        }

        private BackgroundWorkerUserControl<HistoryModel> HistoryUserControl { get; set; }

        public void RefreshData()
        {
            HistoryUserControl.RefreshData();
        }
    }
}

TaskHistoryUserControl XAML:

<UserControl    d:DesignHeight="300"
                d:DesignWidth="300"
                mc:Ignorable="d"
                x:Class="CSharpWPFAppinator.TaskHistoryUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:local="clr-namespace:CSharpWPFAppinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
    <Grid x:Name="TaskHistoryGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

TaskHistoryUserControl CS Code Behind:

using KitchenSinkDLL.Models;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for TaskHistoryUserControl.xaml
    /// </summary>
    public partial class TaskHistoryUserControl : UserControl
    {
        public TaskHistoryUserControl()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            HistoryUserControl = new TaskUserControl<HistoryModel>();
            Grid.SetColumn(HistoryUserControl, 0);
            Grid.SetRow(HistoryUserControl, 0);
            TaskHistoryGrid.Children.Add(HistoryUserControl);
        }

        private TaskUserControl<HistoryModel> HistoryUserControl { get; set; }

        public async Task RefreshData()
        {
            await HistoryUserControl.RefreshData();
        }
    }
}

BWLifeformUserControl XAML:

<UserControl    d:DesignHeight="300"
                d:DesignWidth="300"
                mc:Ignorable="d"
                x:Class="CSharpWPFAppinator.BWLifeformUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:local="clr-namespace:CSharpWPFAppinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
    <Grid x:Name="BWLifeformGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

BWLifeformUserControl CS Code Behind:

using KitchenSinkDLL.Models;
using System.ComponentModel;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for BWLifeformUserControl.xaml
    /// </summary>
    public partial class BWLifeformUserControl : UserControl
    {
        public BWLifeformUserControl()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            LifeformUserControl = new BackgroundWorkerUserControl<LifeformModel>();
            Grid.SetColumn(LifeformUserControl, 0);
            Grid.SetRow(LifeformUserControl, 0);
            BWLifeformGrid.Children.Add(LifeformUserControl);
        }

        private BackgroundWorkerUserControl<LifeformModel> LifeformUserControl { get; set; }

        public void RefreshData()
        {
            LifeformUserControl.RefreshData();
        }
    }
}

TaskLifeformUserControl XAML:

<UserControl    d:DesignHeight="300"
                d:DesignWidth="300"
                mc:Ignorable="d"
                x:Class="CSharpWPFAppinator.TaskLifeformUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:local="clr-namespace:CSharpWPFAppinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
    <Grid x:Name="TaskLifeformGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

TaskLifeformUserControl CS Code Behind:

using KitchenSinkDLL.Models;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for TaskLifeformUserControl.xaml
    /// </summary>
    public partial class TaskLifeformUserControl : UserControl
    {
        public TaskLifeformUserControl()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            LifeformUserControl = new TaskUserControl<LifeformModel>();
            Grid.SetColumn(LifeformUserControl, 0);
            Grid.SetRow(LifeformUserControl, 0);
            TaskLifeformGrid.Children.Add(LifeformUserControl);
        }

        private TaskUserControl<LifeformModel> LifeformUserControl { get; set; }

        public async Task RefreshData()
        {
            await LifeformUserControl.RefreshData();
        }
    }
}

BWPlanetUserControl XAML:

<UserControl    d:DesignHeight="300"
                d:DesignWidth="300"
                mc:Ignorable="d"
                x:Class="CSharpWPFAppinator.BWPlanetUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:local="clr-namespace:CSharpWPFAppinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
    <Grid x:Name="BWPlanetGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

BWPlanetUserControl CS Code Behind:

using KitchenSinkDLL.Models;
using System.ComponentModel;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for BWPlanetUserControl.xaml
    /// </summary>
    public partial class BWPlanetUserControl : UserControl
    {
        public BWPlanetUserControl()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            PlanetUserControl = new BackgroundWorkerUserControl<PlanetModel>();
            Grid.SetColumn(PlanetUserControl, 0);
            Grid.SetRow(PlanetUserControl, 0);
            BWPlanetGrid.Children.Add(PlanetUserControl);
        }

        private BackgroundWorkerUserControl<PlanetModel> PlanetUserControl { get; set; }

        public void RefreshData()
        {
            PlanetUserControl.RefreshData();
        }
    }
}

TaskPlanetUserControl XAML:

<UserControl    d:DesignHeight="300"
                d:DesignWidth="300"
                mc:Ignorable="d"
                x:Class="CSharpWPFAppinator.TaskPlanetUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:local="clr-namespace:CSharpWPFAppinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
    <Grid x:Name="TaskPlanetGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

TaskPlanetUserControl CS Code Behind:

using KitchenSinkDLL.Models;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for TaskPlanetUserControl.xaml
    /// </summary>
    public partial class TaskPlanetUserControl : UserControl
    {
        public TaskPlanetUserControl()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            PlanetUserControl = new TaskUserControl<PlanetModel>();
            Grid.SetColumn(PlanetUserControl, 0);
            Grid.SetRow(PlanetUserControl, 0);
            TaskPlanetGrid.Children.Add(PlanetUserControl);
        }

        private TaskUserControl<PlanetModel> PlanetUserControl { get; set; }

        public async Task RefreshData()
        {
            await PlanetUserControl.RefreshData();
        }
    }
}

BWSpeciesUserControl XAML:

<UserControl    d:DesignHeight="300"
                d:DesignWidth="300"
                mc:Ignorable="d"
                x:Class="CSharpWPFAppinator.BWSpeciesUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:local="clr-namespace:CSharpWPFAppinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
    <Grid x:Name="BWSpeciesGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

BWSpeciesUserControl CS Code Behind:

using KitchenSinkDLL.Models;
using System.ComponentModel;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for BWSpeciesUserControl.xaml
    /// </summary>
    public partial class BWSpeciesUserControl : UserControl
    {
        public BWSpeciesUserControl()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            SpeciesUserControl = new BackgroundWorkerUserControl<SpeciesModel>();
            Grid.SetColumn(SpeciesUserControl, 0);
            Grid.SetRow(SpeciesUserControl, 0);
            BWSpeciesGrid.Children.Add(SpeciesUserControl);
        }

        private BackgroundWorkerUserControl<SpeciesModel> SpeciesUserControl { get; set; }

        public void RefreshData()
        {
            SpeciesUserControl.RefreshData();
        }
    }
}

TaskSpeciesUserControl XAML:

<UserControl    d:DesignHeight="300"
                d:DesignWidth="300"
                mc:Ignorable="d"
                x:Class="CSharpWPFAppinator.TaskSpeciesUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:local="clr-namespace:CSharpWPFAppinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
    <Grid x:Name="TaskSpeciesGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

TaskSpeicesUserControl CS Code Behind:

using KitchenSinkDLL.Models;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for TaskSpeciesUserControl.xaml
    /// </summary>
    public partial class TaskSpeciesUserControl : UserControl
    {
        public TaskSpeciesUserControl()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            SpeciesUserControl = new TaskUserControl<SpeciesModel>();
            Grid.SetColumn(SpeciesUserControl, 0);
            Grid.SetRow(SpeciesUserControl, 0);
            TaskSpeciesGrid.Children.Add(SpeciesUserControl);
        }

        private TaskUserControl<SpeciesModel> SpeciesUserControl { get; set; }

        public async Task RefreshData()
        {
            await SpeciesUserControl.RefreshData();
        }
    }
}

BWVehicleUserControl XAML:

<UserControl    d:DesignHeight="300"
                d:DesignWidth="300"
                mc:Ignorable="d"
                x:Class="CSharpWPFAppinator.BWVehicleUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:local="clr-namespace:CSharpWPFAppinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
    <Grid x:Name="BWVehicleGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

BWVehicleUserControl CS Code Behind:

using KitchenSinkDLL.Models;
using System.ComponentModel;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for BWVehicleUserControl.xaml
    /// </summary>
    public partial class BWVehicleUserControl : UserControl
    {
        public BWVehicleUserControl()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            VehicleUserControl = new BackgroundWorkerUserControl<VehicleModel>();
            Grid.SetColumn(VehicleUserControl, 0);
            Grid.SetRow(VehicleUserControl, 0);
            BWVehicleGrid.Children.Add(VehicleUserControl);
        }

        private BackgroundWorkerUserControl<VehicleModel> VehicleUserControl { get; set; }

        public void RefreshData()
        {
            VehicleUserControl.RefreshData();
        }
    }
}

TaskVehicleUserControl XAML:

<UserControl    d:DesignHeight="300"
                d:DesignWidth="300"
                mc:Ignorable="d"
                x:Class="CSharpWPFAppinator.TaskVehicleUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:local="clr-namespace:CSharpWPFAppinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
    <Grid x:Name="TaskVehicleGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

TaskVehicleUserControl CS Code Behind:

using KitchenSinkDLL.Models;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace CSharpWPFAppinator
{
    /// <summary>
    /// Interaction logic for TaskVehicleUserControl.xaml
    /// </summary>
    public partial class TaskVehicleUserControl : UserControl
    {
        public TaskVehicleUserControl()
        {
            InitializeComponent();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            VehicleUserControl = new TaskUserControl<VehicleModel>();
            Grid.SetColumn(VehicleUserControl, 0);
            Grid.SetRow(VehicleUserControl, 0);
            TaskVehicleGrid.Children.Add(VehicleUserControl);
        }

        private TaskUserControl<VehicleModel> VehicleUserControl { get; set; }

        public async Task RefreshData()
        {
            await VehicleUserControl.RefreshData();
        }
    }
}
WPF using BackgroundWorker vs using Task

ExampleAppinator

Hey all…today I’ll be posting the code for generating my ExampleAppinator project.  It’s the project I’ll be using for all my posts going forward and is something I’ll just continue to grow as I learn new things.  I had been using my Super Duper All Encompassing Wonder App that I’ve been developing for work…but it’s getting to hard to strip out all the PI and NDA stuff before posting to the blog so I decided to just create a special project just for the blog and ExampleAppinator was born.  I’ll be updating this thread whenever I make a change to anything so if anyone actually wants to try the things I’m doing it should be all you need to plug into VS to make things works (assuming you have all the same SDK’s and whatnot I’m using, hehehe).

 

KitchenSink Database:

This database is just a mashing together of any example tables I’ve used in previous posts.  I did my best to fit the tables together in a relational way without having a plan in place ahead of time…it should be usable for future examples though.

Engine Table:

CREATE TABLE [dbo].[Engine]
(
    [Id]            INT IDENTITY(1, 1) NOT NULL,
    [Make]          NVARCHAR(50) NOT NULL,
    [Cylenders]     INT NOT NULL,
    [Displacement]  NVARCHAR(20) NOT NULL,
    [Horsepower]    INT NOT NULL, 

    CONSTRAINT PK_Engine PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT UNIQUE_Engine UNIQUE ([Make], [Cylenders], [Displacement])
)
GO

CREATE NONCLUSTERED INDEX NCI_Make
ON [Engine]([Make] ASC)
GO

CREATE NONCLUSTERED INDEX NCI_Cylenders
ON [Engine]([Cylenders] ASC)
GO

CREATE NONCLUSTERED INDEX NCI_Displacement
ON [Engine]([Displacement] ASC)
GO

CREATE NONCLUSTERED INDEX NCI_Horsepower
ON [Engine]([Horsepower] ASC)

History Table:

CREATE TABLE [dbo].[History]
(
    [Id]            INT IDENTITY(1, 1) NOT NULL,
    [Source]        NVARCHAR(100) NOT NULL,
    [Message]       NVARCHAR(MAX) NOT NULL,
    [LastUpdate]    DATETIME2(7) NOT NULL, 

    CONSTRAINT PK_History PRIMARY KEY CLUSTERED ([Id] ASC)
)
GO

CREATE NONCLUSTERED INDEX NCI_Source
ON [History]([Source] ASC);
GO

CREATE NONCLUSTERED INDEX NCI_LastUpdate
ON [History](LastUpdate ASC);

Lifeform Table:

CREATE TABLE [dbo].[Lifeform]
(
    [Id]            INT IDENTITY(1, 1) NOT NULL,
    [SpeciesId]     INT NOT NULL,
    [PlanetId]      INT NOT NULL,
    [VehicleId]     INT,
    [Name]          NVARCHAR(100) NOT NULL,
    [Age]           INT NOT NULL, 

    CONSTRAINT PK_Lifeform PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT UNIQUE_Lifeform UNIQUE ([SpeciesId], [PlanetId], [Name]),
    CONSTRAINT FK_Lifeform_SpeciesId FOREIGN KEY ([SpeciesId]) REFERENCES [Species]([Id]),
    CONSTRAINT FK_Lifeform_PlanetId FOREIGN KEY ([PlanetId]) REFERENCES [Planet]([Id]),
    CONSTRAINT FK_Lifeform_VehicleId FOREIGN KEY ([VehicleId]) REFERENCES [Vehicle]([Id])
)
GO

CREATE NONCLUSTERED INDEX NCI_SpeciesId
ON [Lifeform]([SpeciesId] ASC)
GO

CREATE NONCLUSTERED INDEX NCI_PlanetId
ON [Lifeform]([PlanetId] ASC)
GO

CREATE NONCLUSTERED INDEX NCI_VehicleId
ON [Lifeform]([VehicleId] ASC)
GO

CREATE NONCLUSTERED INDEX NCI_Name
ON [Lifeform]([Name] ASC)
GO

CREATE NONCLUSTERED INDEX NCI_Age
ON [Lifeform]([Age] ASC)

Planet Table:

CREATE TABLE [dbo].[Planet]
(
    [Id]    INT IDENTITY(1, 1) NOT NULL,
    [Name]  NVARCHAR(100) NOT NULL, 

    CONSTRAINT PK_Planet PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT UNIQUE_Planet UNIQUE ([Name])
)
GO

CREATE NONCLUSTERED INDEX NCI_Name
ON [Planet]([Name] ASC)

Species Table:

CREATE TABLE [dbo].[Species]
(
    [Id]            INT IDENTITY(1, 1) NOT NULL,
    [Name]          NVARCHAR(100) NOT NULL

    CONSTRAINT PK_Species PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT UNIQUE_Species UNIQUE ([Name])
)
GO

CREATE NONCLUSTERED INDEX NCI_Name
ON [Species]([Name] ASC)

Vehicle Table:

CREATE TABLE [dbo].[Vehicle]
(
    [Id]                    INT IDENTITY(1, 1) NOT NULL,
    [EngineId]              INT NOT NULL,
    [Make]                  NVARCHAR(50) NOT NULL,
    [Model]                 NVARCHAR(100) NOT NULL,
    [Year]                  INT NOT NULL,
    [WheelCount]            INT NOT NULL,
    [PassengerCapacity]     INT NOT NULL, 

    CONSTRAINT PK_Vehicle PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT UNIQUE_Vehicle UNIQUE ([EngineId], [Make], [Model], [Year]),
    CONSTRAINT FK_Vehicle_EngineId FOREIGN KEY ([EngineId]) REFERENCES [Engine]([Id])
)
GO

CREATE NONCLUSTERED INDEX NCI_EngineId
ON [Vehicle]([EngineId] ASC)
GO

CREATE NONCLUSTERED INDEX NCI_Make
ON [Vehicle]([Make] ASC)
GO

CREATE NONCLUSTERED INDEX NCI_Model
ON [Vehicle]([Model] ASC)
GO

CREATE NONCLUSTERED INDEX NCI_Year
ON [Vehicle]([Year] ASC)
GO

CREATE NONCLUSTERED INDEX NCI_WheelCount
ON [Vehicle]([WheelCount] ASC)
GO

CREATE NONCLUSTERED INDEX NCI_PassengerCapacity
ON [Vehicle]([PassengerCapacity] ASC)

 

KitchenSinkDLL:

This is a library that other .net projects can reference to get access to the database.  Sure you could go setup EF or Linq to SQL classes to do that…which is what is happening under the covers of KitchenSinkDLL, but this also provides a Model layer which can be easily be used in WPF or MVC apps.  I’ll be posting the code for the C# classes and a snapshot of my Linq To SQL design surface.  I will not be posted the autogen’d code made by Linq To SQL because no one needs to see that mess.  🙂

EngineModel Class:

using KitchenSinkDLL.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace KitchenSinkDLL.Models
{
    public class EngineModel
    {
        public EngineModel()
        {
            Initialize(0, string.Empty, 0, string.Empty, 0);
        }

        public EngineModel(string make, int cylenders, string displacement, int horsepower)
        {
            Initialize(0, make, cylenders, displacement, horsepower);
        }

        public EngineModel(int id)
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            Engine engine = dataContext.Engines
                .SingleOrDefault(e =&amp;amp;amp;gt; e.Id == id);

            if (engine != null)
            {
                Initialize(engine.Id, engine.Make, engine.Cylenders, engine.Displacement, engine.Horsepower);
            }
            else
            {
                Initialize(0, string.Empty, 0, string.Empty, 0);
            }
        }

        public EngineModel(Engine engine)
        {
            if(engine != null)
            {
                Initialize(engine.Id, engine.Make, engine.Cylenders, engine.Displacement, engine.Horsepower);
            }
            else
            {
                Initialize(0, string.Empty, 0, string.Empty, 0);
            }
        }

        private void Initialize(int id, string make, int cylenders, string displacement, int horsepower)
        {
            Id = id;
            Make = make;
            Cylenders = cylenders;
            Displacement = displacement;
            Horsepower = horsepower;
        }

        public int Id { get; set; }

        public string Make { get; set; }

        public int Cylenders { get; set; }

        public string Displacement { get; set; }

        public int Horsepower { get; set; }

        public EngineModel CreateEngine()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;gt; 0 ||
                string.IsNullOrEmpty(Make) ||
                Cylenders &amp;amp;amp;lt;= 0 ||
                string.IsNullOrEmpty(Displacement) ||
                Horsepower &amp;amp;amp;lt;= 0)
            {
                Logger.LogError(callingMethod, "One of the properties is null, empty, or invalid.");
                return null;
            }

            Engine addTarget = new Engine();
            addTarget.Make = Make;
            addTarget.Cylenders = Cylenders;
            addTarget.Displacement = Displacement;
            addTarget.Horsepower = Horsepower;
            dataContext.Engines.InsertOnSubmit(addTarget);

            try
            {
                dataContext.SubmitChanges();
                Id = addTarget.Id;
                HistoryModel.CreateHistoryRecord(callingMethod, $"Created [Engine] with Id: {Id}");
            }
            catch (Exception exception)
            {
                Logger.LogException(callingMethod, exception);
                HistoryModel.CreateHistoryRecord(callingMethod, $"Create [Engine] failed. &amp;amp;nbsp;Error: {exception.Message}");
                return null;
            }

            return this;
        }

        public EngineModel DeleteEngine()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;lt;= 0)             {                 Logger.LogError(callingMethod, "The Id must be greater than 0.");                 return null;             }             Engine deleteTarget = dataContext.Engines                 .SingleOrDefault(e =&amp;amp;amp;gt; e.Id == Id);

            if (deleteTarget != null)
            {
                dataContext.Engines.DeleteOnSubmit(deleteTarget);

                try
                {
                    dataContext.SubmitChanges();
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Deleted [Engine] with Id: {Id}");
                    Id = 0;
                }
                catch (Exception exception)
                {
                    Logger.LogException(callingMethod, exception);
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Delete [Engine] failed. &amp;amp;nbsp;Error: {exception.Message}");
                    return null;
                }
            }
            else
            {
                Logger.LogError(callingMethod, $"No record for the Id: {Id}");
                return null;
            }

            return this;
        }

        public EngineModel UpdateEngine()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;lt;= 0 ||
                string.IsNullOrEmpty(Make) ||
                Cylenders &amp;amp;amp;lt;= 0 ||
                string.IsNullOrEmpty(Displacement) ||
                Horsepower &amp;amp;amp;lt;= 0)             {                 Logger.LogError(callingMethod, "One of the properties is null, empty, or invalid.");                 return null;             }             Engine updateTarget = dataContext.Engines                 .SingleOrDefault(e =&amp;amp;amp;gt; e.Id == Id);

            if (updateTarget != null)
            {
                updateTarget.Make = Make;
                updateTarget.Cylenders = Cylenders;
                updateTarget.Displacement = Displacement;
                updateTarget.Horsepower = Horsepower;

                try
                {
                    dataContext.SubmitChanges();
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Updated [Engine] with Id: {Id}");
                }
                catch (Exception exception)
                {
                    Logger.LogException(callingMethod, exception);
 HistoryModel.CreateHistoryRecord(callingMethod, $"Update [Engine] failed. Error: {exception.Message}");
                    return null;
                }
            }
            else
            {
                Logger.LogError(callingMethod, $"No record for the Id: {Id}");
                return null;
            }

            return this;
        }

        public static IEnumerable&amp;amp;amp;lt;EngineModel&amp;amp;amp;gt; ReadEngines()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);
            return dataContext.Engines.ToEngineModelCollection().OrderBy(e =&amp;amp;amp;gt; e.Id);
        }
    }
}

namespace KitchenSinkDLL
{
    public static partial class Extensions
    {
        public static IEnumerable&amp;amp;amp;lt;EngineModel&amp;amp;amp;gt; ToEngineModelCollection(this IQueryable&amp;amp;amp;lt;Engine&amp;amp;amp;gt; engines)
        {
            List&amp;amp;amp;lt;EngineModel&amp;amp;amp;gt; returnCollection = new List&amp;amp;amp;lt;EngineModel&amp;amp;amp;gt;();

            if (engines != null)
            {
                foreach (var engine in engines)
                {
                    EngineModel newModel = new EngineModel(engine);
                    returnCollection.Add(newModel);
                }
            }

            return returnCollection;
        }
    }
}

HistoryModel Class:

using KitchenSinkDLL.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace KitchenSinkDLL.Models
{
    public class HistoryModel
    {
        public HistoryModel()
        {
            Initialize(0, string.Empty, string.Empty, DateTime.MinValue);
        }

        public HistoryModel(string source, string message)
        {
            Initialize(0, source, message, DateTime.MinValue);
        }

        public HistoryModel(int id)
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            History historyRecord = dataContext.Histories
                .SingleOrDefault(h =&amp;amp;amp;gt; h.Id == id);

            if (historyRecord != null)
            {
                Initialize(historyRecord.Id, historyRecord.Source, historyRecord.Message, historyRecord.LastUpdate);
            }
            else
            {
                Initialize(0, string.Empty, string.Empty, DateTime.MinValue);
            }

        }

        public HistoryModel(History historyRecord)
        {
            if (historyRecord != null)
            {
                Initialize(historyRecord.Id, historyRecord.Source, historyRecord.Message, historyRecord.LastUpdate);
            }
            else
            {
                Initialize(0, string.Empty, string.Empty, DateTime.MinValue);
            }
        }

        private void Initialize(int id, string source, string message, DateTime lastUpdate)
        {
            Id = id;
            Source = source;
            Message = message;
            LastUpdate = lastUpdate;
        }

        public int Id { get; set; }

        public string Source { get; set; }

        public string Message { get; set; }

        public DateTime LastUpdate { get; set; }

        public HistoryModel CreateHistory()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;gt; 0 ||
                string.IsNullOrEmpty(Source) ||
                string.IsNullOrEmpty(Message))
            {
                Logger.LogError(callingMethod, "One of the properties is null, empty, or invalid.");
                return null;
            }

            History addTarget = new History();
            addTarget.Source = Source;
            addTarget.Message = Message;
            addTarget.LastUpdate = DateTime.Now;
            dataContext.Histories.InsertOnSubmit(addTarget);

            try
            {
                dataContext.SubmitChanges();
                Id = addTarget.Id;
                LastUpdate = addTarget.LastUpdate;
            }
            catch (Exception exception)
            {
                Logger.LogException(callingMethod, exception);
                return null;
            }

            return this;
        }

        public HistoryModel DeleteHistory()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;lt;= 0)             {                 Logger.LogError(callingMethod, "The Id must be greater than 0.");                 return null;             }             History deleteTarget = dataContext.Histories                 .SingleOrDefault(h =&amp;amp;amp;gt; h.Id == Id);

            if (deleteTarget != null)
            {
                dataContext.Histories.DeleteOnSubmit(deleteTarget);

                try
                {
                    dataContext.SubmitChanges();
                    Id = 0;
                }
                catch (Exception exception)
                {
                    Logger.LogException(callingMethod, exception);
                    return null;
                }
            }
            else
            {
                Logger.LogError(callingMethod, $"No record for the Id: {Id}");
                return null;
            }

            return this;
        }

        public HistoryModel UpdateHistory()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;lt;= 0 ||                 string.IsNullOrEmpty(Source) ||                 string.IsNullOrEmpty(Message))             {                 Logger.LogError(callingMethod, "One of the properties is null, empty, or invalid.");                 return null;             }             History updateTarget = dataContext.Histories                 .SingleOrDefault(h =&amp;amp;amp;gt; h.Id == Id);

            if (updateTarget != null)
            {
                updateTarget.Source = Source;
                updateTarget.Message = Message;
                updateTarget.LastUpdate = LastUpdate;

                try
                {
                    dataContext.SubmitChanges();
                }
                catch (Exception exception)
                {
                    Logger.LogException(callingMethod, exception);
                    return null;
                }
            }
            else
            {
                Logger.LogError(callingMethod, $"No record for the Id: {Id}");
                return null;
            }

            return this;
        }

        internal static HistoryModel CreateHistoryRecord(string source, string message)
        {
            HistoryModel historyEntry = new HistoryModel(source, message);
            return historyEntry.CreateHistory();
        }

        public static IEnumerable&amp;amp;amp;lt;HistoryModel&amp;amp;amp;gt; ReadHistoryRecords()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);
            return dataContext.Histories.ToHistoryModelCollection().OrderBy(h =&amp;amp;amp;gt; h.Id);
        }

        public static DateTime ReadLastHistoryUpdate()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);
            return dataContext.Histories.Max(h =&amp;amp;amp;gt; h.LastUpdate);
        }
    }
}

namespace KitchenSinkDLL
{
    public static partial class Extensions
    {
        public static IEnumerable&amp;amp;amp;lt;HistoryModel&amp;amp;amp;gt; ToHistoryModelCollection(this IQueryable&amp;amp;amp;lt;History&amp;amp;amp;gt; historyRecords)
        {
            List&amp;amp;amp;lt;HistoryModel&amp;amp;amp;gt; returnCollection = new List&amp;amp;amp;lt;HistoryModel&amp;amp;amp;gt;();

            if (historyRecords != null)
            {
                foreach (var historyRecord in historyRecords)
                {
                    HistoryModel newModel = new HistoryModel(historyRecord);
                    returnCollection.Add(newModel);
                }
            }

            return returnCollection;
        }
    }
}

KitchenSinkDLL Class:

using System.Data;

namespace KitchenSinkDLL
{
    public class KitchenSinkDLL
    {
        internal static KitchenSinkLinqToSqlDataContext CreateDataContext(string callingMethod)
        {
            KitchenSinkLinqToSqlDataContext dataContext = new KitchenSinkLinqToSqlDataContext();

            if (!dataContext.DatabaseExists() &amp;amp;amp;amp;&amp;amp;amp;amp; dataContext.Connection.State != ConnectionState.Open)
            {
                Logger.LogError(callingMethod, "Unable to write to the database.");
                return null;
            }

            return dataContext;
        }
    }
}

LifeformModel Class:

using KitchenSinkDLL.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace KitchenSinkDLL.Models
{
    public class LifeformModel
    {
        public LifeformModel()
        {
            Initialize(0, new SpeciesModel(), new PlanetModel(), new VehicleModel(), string.Empty, 0);
        }

        public LifeformModel(int speciesId, int planetId, int vehicleId, string name, int age)
        {
            Initialize(0, new SpeciesModel(speciesId), new PlanetModel(planetId), new VehicleModel(vehicleId), name, age);
        }

        public LifeformModel(SpeciesModel speciesModel, PlanetModel planetModel, VehicleModel vehicleModel, string name, int age)
        {
            Initialize(0, speciesModel, planetModel, vehicleModel, name, age);
        }

        public LifeformModel(int id, SpeciesModel speciesModel, PlanetModel planetModel, VehicleModel vehicleModel, string name, int age)
        {
            Initialize(id, speciesModel, planetModel, vehicleModel, name, age);
        }
        public LifeformModel(int id)
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            Lifeform lifeform = dataContext.Lifeforms
                .SingleOrDefault(l =&amp;amp;amp;gt; l.Id == id);

            if (lifeform != null)
            {
                Initialize(lifeform.Id, new SpeciesModel(lifeform.Specy), new PlanetModel(lifeform.Planet), new VehicleModel(lifeform.Vehicle), lifeform.Name, lifeform.Age);
            }
            else
            {
                Initialize(0, new SpeciesModel(), new PlanetModel(), new VehicleModel(), string.Empty, 0);
            }
        }

        public LifeformModel(Lifeform lifeform)
        {
            if (lifeform != null)
            {
                Initialize(lifeform.Id, new SpeciesModel(lifeform.Specy), new PlanetModel(lifeform.Planet), new VehicleModel(lifeform.Vehicle), lifeform.Name, lifeform.Age);
            }
            else
            {
                Initialize(0, new SpeciesModel(), new PlanetModel(), new VehicleModel(), string.Empty, 0);
            }
        }

        private void Initialize(int id, SpeciesModel speciesModel, PlanetModel planetModel, VehicleModel vehicleModel, string name, int age)
        {
            Id = id;
            SpeciesModel = speciesModel;
            PlanetModel = planetModel;
            VehicleModel = vehicleModel;
            Name = name;
            Age = age;
        }

        public int Id { get; set; }

        public SpeciesModel SpeciesModel { get; set; }

        public PlanetModel PlanetModel { get; set; }

        public VehicleModel VehicleModel { get; set; }

        public string Name { get; set; }

        public int Age { get; set; }

        public LifeformModel CreateLifeform()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;gt; 0 ||
                SpeciesModel.Id &amp;amp;amp;lt;= 0 ||
                PlanetModel.Id &amp;amp;amp;lt;= 0 ||
                string.IsNullOrEmpty(Name) ||
                Age &amp;amp;amp;lt;= 0)             {                 Logger.LogError(callingMethod, "One of the properties is null, empty, or invalid.");                 return null;             }             Lifeform addTarget = new Lifeform();             addTarget.SpeciesId = SpeciesModel.Id;             addTarget.PlanetId = PlanetModel.Id;             addTarget.Name = Name;             addTarget.Age = Age;             if (VehicleModel.Id &amp;amp;amp;gt; 0)
            {
                addTarget.VehicleId = VehicleModel.Id;
            }

            dataContext.Lifeforms.InsertOnSubmit(addTarget);

            try
            {
                dataContext.SubmitChanges();
                Id = addTarget.Id;
                HistoryModel.CreateHistoryRecord(callingMethod, $"Created [Lifeform] with Id: {Id}");
            }
            catch (Exception exception)
            {
                Logger.LogException(callingMethod, exception);
                HistoryModel.CreateHistoryRecord(callingMethod, $"Create [Lifeform] failed. Error: {exception.Message}");
                return null;
            }

            return this;
        }

        public LifeformModel DeleteLifeform()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;lt;= 0)             {                 Logger.LogError(callingMethod, "The Id must be greater than 0.");                 return null;             }             Lifeform deleteTarget = dataContext.Lifeforms                 .SingleOrDefault(v =&amp;amp;amp;gt; v.Id == Id);

            if (deleteTarget != null)
            {
                dataContext.Lifeforms.DeleteOnSubmit(deleteTarget);

                try
                {
                    dataContext.SubmitChanges();
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Deleted [Lifeform] with Id: {Id}");
                    Id = 0;
                }
                catch (Exception exception)
                {
                    Logger.LogException(callingMethod, exception);
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Delete [Lifeform] failed. Error: {exception.Message}");
                    return null;
                }
            }
            else
            {
                Logger.LogError(callingMethod, $"No record for the Id: {Id}");
                return null;
            }

            return this;
        }

        public LifeformModel UpdateLifeform()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;lt;= 0 ||
                SpeciesModel.Id &amp;amp;amp;lt;= 0 ||
                PlanetModel.Id &amp;amp;amp;lt;= 0 ||
                string.IsNullOrEmpty(Name) ||
                Age &amp;amp;amp;lt;= 0)             {                 Logger.LogError(callingMethod, "One of the properties is null, empty, or invalid.");                 return null;             }             Lifeform updateTarget = dataContext.Lifeforms                 .SingleOrDefault(v =&amp;amp;amp;gt; v.Id == Id);

            if (updateTarget != null)
            {
                updateTarget.SpeciesId = SpeciesModel.Id;
                updateTarget.PlanetId = PlanetModel.Id;
                updateTarget.Name = Name;
                updateTarget.Age = Age;

                if(VehicleModel.Id &amp;amp;amp;gt; 0)
                {
                    updateTarget.VehicleId = VehicleModel.Id;
                }
                else
                {
                    updateTarget.VehicleId = null;
                }

                try
                {
                    dataContext.SubmitChanges();
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Updated [Lifeform] with Id: {Id}");
                }
                catch (Exception exception)
                {
                    Logger.LogException(callingMethod, exception);
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Update [Lifeform] failed. &nbsp;Error: {exception.Message}");
                    return null;
                }
            }
            else
            {
                Logger.LogError(callingMethod, $"No record for the Id: {Id}");
                return null;
            }

            return this;
        }

        public static IEnumerable&amp;amp;amp;lt;LifeformModel&amp;amp;amp;gt; ReadLifeforms()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);
            return dataContext.Lifeforms.ToLifeformModelCollection().OrderBy(v =&amp;amp;amp;gt; v.Id);
        }
    }
}

namespace KitchenSinkDLL
{
    public static partial class Extensions
    {
        public static IEnumerable&amp;amp;amp;lt;LifeformModel&amp;amp;amp;gt; ToLifeformModelCollection(this IQueryable&amp;amp;amp;lt;Lifeform&amp;amp;amp;gt; lifeforms)
        {
            List&amp;amp;amp;lt;LifeformModel&amp;amp;amp;gt; returnCollection = new List&amp;amp;amp;lt;LifeformModel&amp;amp;amp;gt;();

            if (lifeforms != null)
            {
                string callingMethod = MethodBase.GetCurrentMethod().Name;
                KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

                IEnumerable&amp;amp;amp;lt;SpeciesModel&amp;amp;amp;gt; speciesRecords = dataContext.Species.ToSpeciesModelCollection();
                IEnumerable&amp;amp;amp;lt;PlanetModel&amp;amp;amp;gt; planets = dataContext.Planets.ToPlanetModelCollection();
                IEnumerable&amp;amp;amp;lt;VehicleModel&amp;amp;amp;gt; vehicles = dataContext.Vehicles.ToVehicleModelCollection();

                foreach (var lifeform in lifeforms)
                {
                    SpeciesModel speciesModel = speciesRecords.FirstOrDefault(s =&amp;amp;amp;gt; s.Id == lifeform.SpeciesId);
                    PlanetModel planetModel = planets.FirstOrDefault(p =&amp;amp;amp;gt; p.Id == lifeform.PlanetId);
                    VehicleModel vehicleModel = vehicles.FirstOrDefault(v =&amp;amp;amp;gt; v.Id == lifeform.VehicleId);

                    if(vehicleModel == null)
                    {
                        vehicleModel = new VehicleModel();
                    }

                    LifeformModel newModel = new LifeformModel(
                        lifeform.Id,&nbsp;
                        speciesModel,
                        planetModel,
                        vehicleModel,
                        lifeform.Name,
                        lifeform.Age);
                    returnCollection.Add(newModel);
                }
            }

            return returnCollection;
        }
    }
}

Logger Class:

using System;

namespace KitchenSinkDLL
{
    public class Logger
    {
        public static void LogMessage(string callingMethod, string message)
        {
            Console.WriteLine($"{DateTime.Now}: CallingMethod: {callingMethod}");
            Console.WriteLine($"{DateTime.Now}:     Msg      : {message}");
        }

        public static void LogError(string callingMethod, string errorMessage)
        {
            Console.WriteLine($"{DateTime.Now}: CallingMethod: {callingMethod}");
            Console.WriteLine($"{DateTime.Now}:     Error    : {errorMessage}");
        }

        public static void LogException(string callingMethod, Exception exception)
        {
            Console.WriteLine($"{DateTime.Now}: CallingMethod : {callingMethod}");
            Console.WriteLine($"{DateTime.Now}: Exception     : {exception.GetType().Name}");
            Console.WriteLine($"{DateTime.Now}:     Message   : {exception.Message}");
            Console.WriteLine($"{DateTime.Now}:     Source    : {exception.Source}");
            Console.WriteLine($"{DateTime.Now}:     StackTrace: {exception.StackTrace}");
        }
    }
}

PlanetModel Class:

using KitchenSinkDLL.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace KitchenSinkDLL.Models
{
    public class PlanetModel
    {
        public PlanetModel()
        {
            Initialize(0, string.Empty);
        }

        public PlanetModel(string name)
        {
            Initialize(0, name);
        }

        public PlanetModel(int id)
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            Planet planet = dataContext.Planets
                .SingleOrDefault(p =&amp;amp;amp;gt; p.Id == id);

            if (planet != null)
            {
                Initialize(planet.Id, planet.Name);
            }
            else
            {
                Initialize(0, string.Empty);
            }
        }

        public PlanetModel(Planet planet)
        {
            if(planet != null)
            {
                Initialize(planet.Id, planet.Name);
            }
            else
            {
                Initialize(0, string.Empty);
            }
        }

        private void Initialize(int id, string name)
        {
            Id = id;
            Name = name;
        }

        public int Id { get; set; }

        public string Name { get; set; }

        public PlanetModel CreatePlanet()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;gt; 0 ||
                string.IsNullOrEmpty(Name))
            {
                Logger.LogError(callingMethod, "One of the properties is null, empty, or invalid.");
                return null;
            }

            Planet addTarget = new Planet();
            addTarget.Name = Name;
            dataContext.Planets.InsertOnSubmit(addTarget);

            try
            {
                dataContext.SubmitChanges();
                Id = addTarget.Id;
                HistoryModel.CreateHistoryRecord(callingMethod, $"Created [Planet] with Id: {Id}");
            }
            catch (Exception exception)
            {
                Logger.LogException(callingMethod, exception);
                HistoryModel.CreateHistoryRecord(callingMethod, $"Create [Planet] failed. &nbsp;Error: {exception.Message}");
                return null;
            }

            return this;
        }

        public PlanetModel DeletePlanet()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;lt;= 0)             {                 Logger.LogError(callingMethod, "The Id must be greater than 0.");                 return null;             }             Planet deleteTarget = dataContext.Planets                 .SingleOrDefault(p =&amp;amp;amp;gt; p.Id == Id);

            if (deleteTarget != null)
            {
                dataContext.Planets.DeleteOnSubmit(deleteTarget);

                try
                {
                    dataContext.SubmitChanges();
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Deleted [Planet] with Id: {Id}");
                    Id = 0;
                }
                catch (Exception exception)
                {
                    Logger.LogException(callingMethod, exception);
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Delete [Planet] failed. &nbsp;Error: {exception.Message}");
                    return null;
                }
            }
            else
            {
                Logger.LogError(callingMethod, $"No record for the Id: {Id}");
                return null;
            }

            return this;
        }

        public PlanetModel UpdatePlanet()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;lt;= 0 ||                 string.IsNullOrEmpty(Name))             {                 Logger.LogError(callingMethod, "One of the properties is null, empty, or invalid.");                 return null;             }             Planet updateTarget = dataContext.Planets                 .SingleOrDefault(p =&amp;amp;amp;gt; p.Id == Id);

            if (updateTarget != null)
            {
                updateTarget.Name = Name;

                try
                {
                    dataContext.SubmitChanges();
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Updated [Planet] with Id: {Id}");
                }
                catch (Exception exception)
                {
                    Logger.LogException(callingMethod, exception);
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Update [Planet] failed. &nbsp;Error: {exception.Message}");
                    return null;
                }
            }
            else
            {
                Logger.LogError(callingMethod, $"No record for the Id: {Id}");
                return null;
            }

            return this;
        }

        public static IEnumerable&amp;amp;amp;lt;PlanetModel&amp;amp;amp;gt; ReadPlanets()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);
            return dataContext.Planets.ToPlanetModelCollection().OrderBy(p =&amp;amp;amp;gt; p.Id);
        }
    }
}

namespace KitchenSinkDLL
{
    public static partial class Extensions
    {
        public static IEnumerable&amp;amp;amp;lt;PlanetModel&amp;amp;amp;gt; ToPlanetModelCollection(this IQueryable&amp;amp;amp;lt;Planet&amp;amp;amp;gt; planets)
        {
            List&amp;amp;amp;lt;PlanetModel&amp;amp;amp;gt; returnCollection = new List&amp;amp;amp;lt;PlanetModel&amp;amp;amp;gt;();

            if (planets != null)
            {
                foreach (var planet in planets)
                {
                    PlanetModel newModel = new PlanetModel(planet);
                    returnCollection.Add(newModel);
                }
            }

            return returnCollection;
        }
    }
}

SpeciesModel Class:

using KitchenSinkDLL.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace KitchenSinkDLL.Models
{
    public class SpeciesModel
    {
        public SpeciesModel()
        {
            Initialize(0, string.Empty);
        }

        public SpeciesModel(string name)
        {
            Initialize(0, name);
        }

        public SpeciesModel(int id)
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            Specy specy = dataContext.Species
                .SingleOrDefault(s =&amp;amp;amp;gt; s.Id == id);

            if (specy != null)
            {
                Initialize(specy.Id, specy.Name);
            }
            else
            {
                Initialize(0, string.Empty);
            }
        }

        public SpeciesModel(Specy specy)
        {
            if(specy != null)
            {
                Initialize(specy.Id, specy.Name);
            }
            else
            {
                Initialize(0, string.Empty);
            }
        }

        private void Initialize(int id, string name)
        {
            Id = id;
            Name = name;
        }

        public int Id { get; set; }

        public string Name { get; set; }

        public SpeciesModel CreateSpecies()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;gt; 0 ||
                string.IsNullOrEmpty(Name))
            {
                Logger.LogError(callingMethod, "One of the properties is null, empty, or invalid.");
                return null;
            }

            Specy addTarget = new Specy();
            addTarget.Name = Name;
            dataContext.Species.InsertOnSubmit(addTarget);

            try
            {
                dataContext.SubmitChanges();
                Id = addTarget.Id;
                HistoryModel.CreateHistoryRecord(callingMethod, $"Created [Species] with Id: {Id}");
            }
            catch (Exception exception)
            {
                Logger.LogException(callingMethod, exception);
                HistoryModel.CreateHistoryRecord(callingMethod, $"Create [Species] failed. &nbsp;Error: {exception.Message}");
                return null;
            }

            return this;
        }

        public SpeciesModel DeleteSpecies()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;lt;= 0)             {                 Logger.LogError(callingMethod, "The Id must be greater than 0.");                 return null;             }             Specy deleteTarget = dataContext.Species                 .SingleOrDefault(s =&amp;amp;amp;gt; s.Id == Id);

            if (deleteTarget != null)
            {
                dataContext.Species.DeleteOnSubmit(deleteTarget);

                try
                {
                    dataContext.SubmitChanges();
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Deleted [Species] with Id: {Id}");
                    Id = 0;
                }
                catch (Exception exception)
                {
                    Logger.LogException(callingMethod, exception);
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Delete [Species] failed. &nbsp;Error: {exception.Message}");
                    return null;
                }
            }
            else
            {
                Logger.LogError(callingMethod, $"No record for the Id: {Id}");
                return null;
            }

            return this;
        }

        public SpeciesModel UpdateSpecies()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;lt;= 0 ||                 string.IsNullOrEmpty(Name))             {                 Logger.LogError(callingMethod, "One of the properties is null, empty, or invalid.");                 return null;             }             Specy updateTarget = dataContext.Species                 .SingleOrDefault(s =&amp;amp;amp;gt; s.Id == Id);

            if (updateTarget != null)
            {
                updateTarget.Name = Name;

                try
                {
                    dataContext.SubmitChanges();
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Updated [Species] with Id: {Id}");
                }
                catch (Exception exception)
                {
                    Logger.LogException(callingMethod, exception);
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Update [Species] failed. &nbsp;Error: {exception.Message}");
                    return null;
                }
            }
            else
            {
                Logger.LogError(callingMethod, $"No record for the Id: {Id}");
                return null;
            }

            return this;
        }

        public static IEnumerable&amp;amp;amp;lt;SpeciesModel&amp;amp;amp;gt; ReadPlanets()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);
            return dataContext.Species.ToSpeciesModelCollection().OrderBy(s =&amp;amp;amp;gt; s.Id);
        }
    }
}

namespace KitchenSinkDLL
{
    public static partial class Extensions
    {
        public static IEnumerable&amp;amp;amp;lt;SpeciesModel&amp;amp;amp;gt; ToSpeciesModelCollection(this IQueryable&amp;amp;amp;lt;Specy&amp;amp;amp;gt; speciesRecords)
        {
            List&amp;amp;amp;lt;SpeciesModel&amp;amp;amp;gt; returnCollection = new List&amp;amp;amp;lt;SpeciesModel&amp;amp;amp;gt;();

            if (speciesRecords != null)
            {
                foreach (var species in speciesRecords)
                {
                    SpeciesModel newModel = new SpeciesModel(species);
                    returnCollection.Add(newModel);
                }
            }

            return returnCollection;
        }
    }
}

VehicleModel Class:

using KitchenSinkDLL.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace KitchenSinkDLL.Models
{
    public class VehicleModel
    {
        public VehicleModel()
        {
            Initialize(0, new EngineModel(), string.Empty, string.Empty, 0, 0, 0);
        }

        public VehicleModel(int engineId, string make, string model, int year, int wheelCount, int passengerCapacity)
        {
            Initialize(0, new EngineModel(engineId), make, model, year, wheelCount, passengerCapacity);
        }

        public VehicleModel(EngineModel engineModel, string make, string model, int year, int wheelCount, int passengerCapacity)
        {
            Initialize(0, engineModel, make, model, year, wheelCount, passengerCapacity);
        }

&nbsp;       public VehicleModel(int id, EngineModel engineModel, string make, string model, int year, int wheelCount, int passengerCapacity)
        {
            Initialize(id, engineModel, make, model, year, wheelCount, passengerCapacity);
        }
        public VehicleModel(int id)
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            Vehicle vehicle = dataContext.Vehicles
                .SingleOrDefault(v =&amp;amp;amp;gt; v.Id == id);

            if (vehicle != null)
            {
                Initialize(vehicle.Id, new EngineModel(vehicle.Engine), vehicle.Make, vehicle.Model, vehicle.Year, vehicle.WheelCount, vehicle.PassengerCapacity);
            }
            else
            {
                Initialize(0, new EngineModel(), string.Empty, string.Empty, 0, 0, 0);
            }
        }

        public VehicleModel(Vehicle vehicle)
        {
            if (vehicle != null)
            {
                Initialize(vehicle.Id, new EngineModel(vehicle.Engine), vehicle.Make, vehicle.Model, vehicle.Year, vehicle.WheelCount, vehicle.PassengerCapacity);
            }
            else
            {
                Initialize(0, new EngineModel(), string.Empty, string.Empty, 0, 0, 0);
            }
        }

        private void Initialize(int id, EngineModel engineModel, string make, string model, int year, int wheelCount, int passengerCapacity)
        {
            Id = id;
            EngineModel = engineModel;
            Make = make;
            Model = model;
            Year = year;
            WheelCount = wheelCount;
            PassengerCapacity = passengerCapacity;
        }

        public int Id { get; set; }

        public EngineModel EngineModel { get; set; }

        public string Make { get; set; }

        public string Model { get; set; }

        public int Year { get; set; }

        public int WheelCount { get; set; }

        public int PassengerCapacity { get; set; }

        public VehicleModel CreateVehicle()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;gt; 0 ||
                EngineModel.Id &amp;amp;amp;lt;= 0 ||
                string.IsNullOrEmpty(Make) ||
                string.IsNullOrEmpty(Model) ||
                Year &amp;amp;amp;lt;= 0 ||
                WheelCount &amp;amp;amp;lt;= 0 ||
                PassengerCapacity &amp;amp;amp;lt;= 0)
            {
                Logger.LogError(callingMethod, "One of the properties is null, empty, or invalid.");
                return null;
            }

            Vehicle addTarget = new Vehicle();
            addTarget.EngineId = EngineModel.Id;
            addTarget.Make = Make;
            addTarget.Model = Model;
            addTarget.Year = Year;
            addTarget.WheelCount = WheelCount;
            addTarget.PassengerCapacity = PassengerCapacity;
            dataContext.Vehicles.InsertOnSubmit(addTarget);

            try
            {
                dataContext.SubmitChanges();
                Id = addTarget.Id;
                HistoryModel.CreateHistoryRecord(callingMethod, $"Created [Vehicle] with Id: {Id}");
            }
            catch (Exception exception)
            {
                Logger.LogException(callingMethod, exception);
                HistoryModel.CreateHistoryRecord(callingMethod, $"Create [Vehicle] failed. &nbsp;Error: {exception.Message}");
                return null;
            }

            return this;
        }

        public VehicleModel DeleteVehicle()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;lt;= 0)             {                 Logger.LogError(callingMethod, "The Id must be greater than 0.");                 return null;             }             Vehicle deleteTarget = dataContext.Vehicles                 .SingleOrDefault(v =&amp;amp;amp;gt; v.Id == Id);

            if (deleteTarget != null)
            {
                dataContext.Vehicles.DeleteOnSubmit(deleteTarget);

                try
                {
                    dataContext.SubmitChanges();
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Deleted [Vehicle] with Id: {Id}");
                    Id = 0;
                }
                catch (Exception exception)
                {
                    Logger.LogException(callingMethod, exception);
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Delete [Vehicle] failed. &nbsp;Error: {exception.Message}");
                    return null;
                }
            }
            else
            {
                Logger.LogError(callingMethod, $"No record for the Id: {Id}");
                return null;
            }

            return this;
        }

        public VehicleModel UpdateVehicle()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);

            if (Id &amp;amp;amp;lt;= 0 ||
                EngineModel.Id &amp;amp;amp;lt;= 0 ||
                string.IsNullOrEmpty(Make) ||
                string.IsNullOrEmpty(Model) ||
                Year &amp;amp;amp;lt;= 0 ||
                WheelCount &amp;amp;amp;lt;= 0 ||
                PassengerCapacity &amp;amp;amp;lt;= 0)             {                 Logger.LogError(callingMethod, "One of the properties is null, empty, or invalid.");                 return null;             }             Vehicle updateTarget = dataContext.Vehicles                 .SingleOrDefault(v =&amp;amp;amp;gt; v.Id == Id);

            if (updateTarget != null)
            {
                updateTarget.EngineId = EngineModel.Id;
                updateTarget.Make = Make;
                updateTarget.Model = Model;
                updateTarget.Year = Year;
                updateTarget.WheelCount = WheelCount;
                updateTarget.PassengerCapacity = PassengerCapacity;

                try
                {
                    dataContext.SubmitChanges();
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Updated [Vehicle] with Id: {Id}");
                }
                catch (Exception exception)
                {
                    Logger.LogException(callingMethod, exception);
                    HistoryModel.CreateHistoryRecord(callingMethod, $"Update [Vehicle] failed. &nbsp;Error: {exception.Message}");
                    return null;
                }
            }
            else
            {
                Logger.LogError(callingMethod, $"No record for the Id: {Id}");
                return null;
            }

            return this;
        }

        public static IEnumerable&amp;amp;amp;lt;VehicleModel&amp;amp;amp;gt; ReadVehicles()
        {
            string callingMethod = MethodBase.GetCurrentMethod().Name;
            KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);
            return dataContext.Vehicles.ToVehicleModelCollection().OrderBy(v =&amp;amp;amp;gt; v.Id);
        }
    }
}

namespace KitchenSinkDLL
{
    public static partial class Extensions
    {
        public static IEnumerable&amp;amp;amp;lt;VehicleModel&amp;amp;amp;gt; ToVehicleModelCollection(this IQueryable&amp;amp;amp;lt;Vehicle&amp;amp;amp;gt; vehicles)
        {
            List&amp;amp;amp;lt;VehicleModel&amp;amp;amp;gt; returnCollection = new List&amp;amp;amp;lt;VehicleModel&amp;amp;amp;gt;();

            if (vehicles != null)
            {
                string callingMethod = MethodBase.GetCurrentMethod().Name;
                KitchenSinkLinqToSqlDataContext dataContext = KitchenSinkDLL.CreateDataContext(callingMethod);
                IEnumerable&amp;amp;amp;lt;EngineModel&amp;amp;amp;gt; engines = dataContext.Engines.ToEngineModelCollection();

                foreach (var vehicle in vehicles)
                {
                    EngineModel engineModel = engines.FirstOrDefault(e =&amp;amp;amp;gt; e.Id == vehicle.EngineId);
                    VehicleModel newModel = new VehicleModel(
                        vehicle.Id,&nbsp;
                        engineModel,
                        vehicle.Model,
                        vehicle.Model,
                        vehicle.Year,
                        vehicle.WheelCount,
                        vehicle.PassengerCapacity);
                    returnCollection.Add(newModel);
                }
            }

            return returnCollection;
        }
    }
}

 

KitchenSinkClogger Program:

This isn’t anything special…it just fills the database up with random junk to use for showing data.  You may get some relational errors if it randomly tries to insert the same record twice…but it’s ok to just ignore them and let it fill the database up with whatever.

Program Class:

using KitchenSinkDLL.Models;
using System;
using System.Collections.Generic;
using System.Linq;

namespace KitchenSinkClogger
{
    public class Program
    {
        static void Main(string[] args)
        {
            Random rnd = new Random(DateTime.Now.Millisecond);
            KitchenSinkDLL.KitchenSinkDLL.ConnectionString = @"Data Source=cmd-roswell;Initial Catalog=KitchenSink;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";

            for(int i = 0; i < 1000000; i++)
            {
                EngineModel newEngineModel = new EngineModel($"{rnd.Next(i)}EngineMake{rnd.Next(i)}", rnd.Next(1, 12), $"Disp{rnd.Next(1, 10000)}", rnd.Next(1, 1200));
                newEngineModel.CreateEngine();

                List<EngineModel> engines = EngineModel.ReadEngines().ToList();
                EngineModel engine = engines[rnd.Next(engines.Count - 1)];
                VehicleModel newVehicleModel = new VehicleModel(engine, $"{rnd.Next(i)}Make{rnd.Next(i)}", $"{rnd.Next(i)}Model{rnd.Next(i)}", rnd.Next(1, 2016), rnd.Next(2, 18), rnd.Next(1, 8));
                newVehicleModel.CreateVehicle();

                PlanetModel newPlanetModel = new PlanetModel($"{rnd.Next(i)}Planet{rnd.Next(i)}");
                newPlanetModel.CreatePlanet();

                SpeciesModel newSpeciesModel = new SpeciesModel($"{rnd.Next(i)}Species{rnd.Next(i)}");
                newSpeciesModel.CreateSpecies();

                List<PlanetModel> planets = PlanetModel.ReadPlanets().ToList();
                PlanetModel planet = planets[rnd.Next(planets.Count - 1)];
                List<SpeciesModel> speciesRecords = SpeciesModel.ReadPlanets().ToList();
                SpeciesModel species = speciesRecords[rnd.Next(speciesRecords.Count - 1)];

                VehicleModel vehicle = null;

                if(rnd.Next(i) > i / 2)
                {
                    List<VehicleModel> vehicles = VehicleModel.ReadVehicles().ToList();
                    vehicle = vehicles[rnd.Next(vehicles.Count - 1)];
                }

                LifeformModel newLifeformModel = new LifeformModel(species, planet, vehicle, $"{rnd.Next(i)}Lifeform{rnd.Next(i)}", rnd.Next(1, 200));
                newLifeformModel.CreateLifeform();
            }
        }
    }
}

 

That’s pretty much it for now.  I’ll be adding a MVC and WPF app into the project later.  Also feel free to critque the projects I’ve got…I’m always open to advice about how I’m doing stuff.  🙂  Enjoy!

ExampleAppinator

Freakin Fitness

Hello all.

My main goal for this blog was to talk about coding stuff…and I’m working on some stuff for that right now but there’s that pesky work stuff that gets in the way of me creating my examples and crafting up blog posts to go with them.  Whine whine whine…I know.  You’re all chomping at the bit for those blog posts I know…they’re coming.

In the meantime…I also have wrote about some musical thoughts to fill the gaps.  I have lots more to say about music as well, but the mood has to strike me at the right time to blog about it and I’m never in a convienent spot to blog when I’m struck with inspiration about music.  It’s usually whilie I’m driving.

Another area of my life that I spend a lot of time thinking about recently is my fitness.  And that’s what we’ve gathered here today to chat about.  I’ve generally used Facebook in the past to post any deep and meaningful thoughts about anything…I think going forward I’ll tend to use this blog for that sort of thing.  That being said…the category for today is Freakin Fitness.

So what do I mean about Freakin Fitness?  It means I hate working out…I hate sweating…I am inherintly lazy and I like to habitually snack on things.  My nature is to want to sit and hermit into a little cave and binge watch videos or play video games until there’s something else I have to do.  It’s not a healthy nature, I’ll admit…but then again nothing that can be obssessed over is healthy in the long run…so since I’m a smrt (now read that in Homer Simpson’s voice…I am so smart….S.M.R.T.) human being…I know I should fight that nature.

I promise that future posts on this topic will be shorter and more to the point, but this is kinda a history post so when I get old and alzheimery I can read this and remember…so…anyway…

I will admit I let myself go a bit over the years.  At the time of this writing I’m 6′ 4″ and 314 lbs.  I didn’t weight myself when I started kung fu…I’m estimating that I was somewhere around 350-360 lbs.  I was a much more addicted of a gamer when I was single man than I am now as a family man…however I also lost weight a lot easier in those days.  Back then really all I had to do is eat 1 less cheeseburger and take a walk aro0und the block (ok, I’m exagurating, but you get the idea…).  These days…my metabolism has slowed significantly.  Now I have to be very careful about what I eat…and I need to workout like mad to shed any weight.

One of the things I’ve come to realize as I wisen up in my family man role is that life is short.  Very short.  And if I give into my nature it would get shorter.  I want every moment I can squeeze into my life to enjoy my family.  I want to be there for them for as long as I can possibly last to help support them and raise them up to be the best they can be.  I have already exceeded every goal I have ever set for myself, so now I focus on them.  I want nothing more than for my kids to surpass me in everything I’ve managed to accomplish, but I refuse to make it an easy target for them…and I want to last as long as I can to be there with my wife to watch it all unfold.

So to that end I’ve once again found the willpower to ignore my dislike of working out and sweating…and in general getting off my lazy ass.  I found that willpower through my family in wanting to be a good role model for my boys.  Actually it manifested about 1 1/2 years ago when I got my younger boys and I into kung fu…but it wasn’t nearly as rampant as it is now.  Back then I just opened the door and peaked in…now I can confidently say I’ve taken a step through the door…but I occasionally peak back out.

When I got started in kung fu, like I said, it was to start down a path to get in shape to be a better role model.  As I attended the classes and grew to become friends with Sifu and the other students I have found other reason that have inspired me to get into shape, but honeslty when I think deeply about it…it always roots back to wanting to do it for my family.

Anyway…a couple months after I started kung fu, I started watching several of my fellow classmates do an extra “grappling” class…and then later compete in grappling tournements.  For some reason ground fighting has always seemed right up my alley.  I’m a big ol’ bear…I think it’s just another part of my nature to find it to my liking.  I got a lot of weight to throw around and I like to think I have a good feel for being able to shift it around and use it (of course Sifu and my fellow classmates often show me how much I suck at it, but that’s a post for another time).  Anyway…the story goes that I eventually worked up the nerve to ask Sifu if I could join in on the grappling fun…he asked if I was sure because it was kinda the “deep end of the pool” and as I’m typing this I can hear his words echo through my mind…”be careful what you wish for”.

I first started on just doing the Thursday night before kung fu class.  No offical style…just Sifu showing us what he’s learned about fighting on the ground over the years, which was then enhanced by Tim’s amazing jui jitsui skills…but really it’s just generic ground fighting tactics.  I love it.  So I rolled with these guys for a couple months, then I went with my younger boys and watched my fellow classmates compete in the tourney…and that’s where my life turned into Freakin Fitness.  I felt compelled to take part.  I formulated a plan to help me focus my willpower.  I decided I was going to compete in this grappling tourney.

What’s the big deal with that?  The big deal is I’ve never done any sort of sports competition before.  I’ve done some competeing in high school concert band…but I don’t think that compares…and the other thing I was 350 lbs.  I had some obvious obsticles and I ended up seeing this as an amazing oppertunity to show my kids how to perservere and accomplish a difficult goal.  I just had to…you know…do it.  Eeeek!

Now even though I’m a lazy schlub…my nature is also that of a competitor.  I want to win when I play a game.  When I loose I try harder to win the next time.  If I keep loosing I practice until I can come back and triumph…I’ve just never applied that to any actual physical competition…so I’m doing what I can to take this seriously as possible.

I’m looking to compete in Novemeber which at the moment is about 6 1/2 months away…I’m actually pretty scared about competeing but I’m going to go through with it no matter what.  As much as I’m going to want to be a winner in my match, I trying hard to remember that my goal isn’t to win, it’s just to compete…because come on…going from a lard ass 350 lbs who was huffing and puffing after climbing stairs…down to what I’m hoping is somewhere below 300 (currently at 314) and now able to grapple in class for 45 mins with a little left in the tank afterwords…that’s a victory all in itself.

To train I’ve been mostly doing my regular Thursday grappling class…and when the other students are preparing to compete I join in their extra jui jitsu classes to get extra time in on the matt.  I think all I should really be worrying about really is my cardio…but I’m hoping that some skills will stick in my head the more time I get on the matt between now and November.  Currently there’s no other training going on with fellow students so I started rolling with another school on Friday’s.  They are a great group of folks who are at the very beginings of starting a Gracie Jui Jitsu class.  I’ll gleen what I can from it…but my heart and loyalty will always belong to the Kung Fu club.

Also to help with training I’ve converted one of the rooms in my house into a workout room.  I got ahold of an eliptical and now have a heavy bag hung up to allow me to workout at home instead of only at the kung fu compound.  Any day that I don’t workout in some sort of class, I spend 20 minutes on my eliptical keeping my heart rate up around 150 beats per minutes…and then I’ll jump off and punch the bag for about 10 minutes.  That’ll pretty much have my shirt drenched in sweat when I’m done.  The next step is to add some matts on the floors.

“Be careful what you wish for”…yup…since I started my on my journey I’ve I inured myself more times that I can count…I’ve worked out so hard I’ve thrown up several times…I’ve soaked my shirt completely drenched in sweat hundreds of times…I’ve woken up sore pretty much every single day.  I have cursed this decision and have been tempted to give up many times…but giving up is not what being a family man is about.  Like I mentioned before, I’m providing a target for my boys to surpass…and I’m not making it easy…but I hope that they’ll reach for it and blow it to smithereens.

So that’s the history of what I’m talking about whenever I put up a Freakin Fitness category.  It will often be ranting and depressing to be sure…but it’s a pressure valve I’ll use to help release every once in a while so I don’t blow my top and just give up.  Enjoy the adventure.  🙂

Freakin Fitness

Working Topics

Hello again (again again again…..)…it is I…the brain that dwells within Toffer’s very spacious cranium.

I’m not through with this blog yet…not by a longshot.  🙂  Going forward I’ll be using this post to keep track of the topics I’m currently blogging about or plan to blog about…so I’ll be continuously editing this post as I go.

As of this very second, here are the topics I have in my brain…Keep tuned and as always, Enjoy!  🙂

Current Topics:

These are topics that I’m currently blogging about and actively updating the posts because I’m not 100% finished yet.  Once a post is 100% complete I’ll move it off this list and it’ll just be it’s own thing at that point.

  • Evolvinator – What the hell?  Something new!?!  Yup.  I’m getting my head wrapped around something new and I intend to start posting about it.  It’s stupid and probably only interesting to me…but something I said was helpful to someone once, so what the hell.  Evolvinator is going to be a little project app I’m going to let “evolve” to teach myself things about evolution.  I’m going to try and keep maticulous track of it’s progression so I can do an over-time sort of post-mordem sort of thing.  Lets see if I can actually keep myself disciplined enough to do it.  😛
  • Switching from using BackgroundWorker to using Task in a WPF app
  • Binding a Telerik Kendo Grid column that has a null value object when trying use a sub property for displaying the value
  • What’s this SignalR stuff anyway?
    • My work is having me do investigation into how to incorperate SignalR into what we do so I’ve got a few samples to blog about here.  Nifty stuff.  I’m going to enjoy adding it into my superduper omega supreme wonder app.
  • http://www.telerik.com/forums/rebind-combobox-after-disable-enable-sequence#OXB3jC1RGkOfMCS1Z1FXiw
    • I bumped into this and it’s about enabling and disabling kendo comboboxes, but it pretty much applies to all kendo controls
  • Regex tricks
    • I’ve recently had to learn how to use regex queries…like a lot of them, so I’m going to post some of the tricks I used to get some of the data I was looking for in a given string.
    • I’ll also explain in length about how great the site www.regex101.com is.  🙂

Retired Topics:

These are topics that I was thinking about digging into, but for one reason or another decided it wouldn’t be worth my time…the “back burner” so to speak…

  • Making a WPF app using python for code behind instead of C#
    • I did some work on this and got a project setup enough to actually create a page while using python code behind…I’d be willing to make a post about the experience if anyone had interest…but mostly what I found is that while it seems like a novel idea at first, it’s just easier to let C# do what it already does well with WPF and let python do what it’s good at instead of trying to marry them…so I’m not going to be doing any more investigation down this path.

 

Working Topics

Setting a Custom WPF ToolTip and how to keep it shown…

Ok, so to be perfectly honest I was a little bored at work and decided to experiment with my super duper omega wonder app (now with kitchen sink features).  My experiment was to put some ToolTip pop ups of historical data about whatever cell the mouse happened to bre hovering over…so I started whipping up a test app to figure out how to do that…and you the lucky reader can follow my journey.  I should be charging for this, I know but we can chalk it up to my humanitarian contribution to our species for now.  *drip drip drip of sarcasim*

Ok, so in our test app we need a WPF window with a basic generic grid on it.  I’ve been using Telerik WPF controls, but everything I do in this post should work just as well with standard .NET WPF controls as well.  Telerik isn’t doing anything special to ToolTips and AFAIK they just expose the underlying .NET stuff.  Anyway, my examples will all be with Telerik…so take it FWIW.

First I’m going to make up some ficticious relational data.  It’s totally meaningless but will be useful to test things with.  So I’m going to be tracking scores of players who participate in multiple sports.  Here are the classes I’ve defined to store the data.

Player class:

public class Player
{
    public string UniqueName { get; set; }

    public string SportName { get; set; }

    public string Score { get; set; }

    public DateTime AchievedDate { get; set; }
}

SportContest class and the classes inheriting from it:

public class SportContest
{
    public List&amp;lt;Player&amp;gt; Contestants { get; set; }

    public IEnumerable&amp;lt;Player&amp;gt; GetResults()
    {
        if (Contestants == null)
        {
            return null;
        }
        else
        {
            return Contestants.Where(c =&gt; c.SportName == this.GetType().Name);
        }
    }
}

public class Hockey : SportContest
{
    public void AddScore(string playerUniqueName)
    {
        if(Contestants == null)
        {
            Contestants = new List&lt;Player&gt;();
        }

        Player player = Contestants.FirstOrDefault(c =&gt;
            c.UniqueName == playerUniqueName &amp;&amp;
            c.SportName == this.GetType().Name);

        if (player == null)
        {
            player = new Player();
            player.UniqueName = playerUniqueName;
            player.SportName = this.GetType().Name;
            player.Score = "1 goal";
            player.AchievedDate = DateTime.Now;

            Contestants.Add(player);
        }
        else
        {
            int scoreValue = Convert.ToInt32(player.Score.Substring(0, player.Score.IndexOf(" ")));
            player.Score = $"{++scoreValue} goals";
        }
    }
}

public class Baseball : SportContest
{
    public void AddScore(string playerUniqueName)
    {
        if (Contestants == null)
        {
            Contestants = new List&lt;Player&gt;();
        }

        Player player = Contestants.FirstOrDefault(c =&gt;
            c.UniqueName == playerUniqueName &amp;&amp;
            c.SportName == this.GetType().Name);

        if (player == null)
        {
            player = new Player();
            player.UniqueName = playerUniqueName;
            player.SportName = this.GetType().Name;
            player.Score = "1 run";
            player.AchievedDate = DateTime.Now;

            Contestants.Add(player);
        }
        else
        {
            int scoreValue = Convert.ToInt32(player.Score.Substring(0, player.Score.IndexOf(" ")));
            player.Score = $"{++scoreValue} runs";
        }
    }
}

public class Football : SportContest
{
    public void AddScore(string playerUniqueName, int pointsScored)
    {
        if (Contestants == null)
        {
            Contestants = new List&lt;Player&gt;();
        }

        Player player = Contestants.FirstOrDefault(c =&gt;
            c.UniqueName == playerUniqueName &amp;&amp;
            c.SportName == this.GetType().Name);

        if (player == null)
        {
            player = new Player();
            player.UniqueName = playerUniqueName;
            player.SportName = this.GetType().Name;
            player.Score = $"{pointsScored} {(pointsScored &gt; 1 ? " points" : " point")}";
            player.AchievedDate = DateTime.Now;

            Contestants.Add(player);
        }
        else
        {
            int scoreValue = Convert.ToInt32(player.Score.Substring(0, player.Score.IndexOf(" ")));
            scoreValue = scoreValue + pointsScored;
            player.Score = $"{scoreValue} points";
        }
    }
}

public class Basketball : SportContest
{
    public void AddScore(string playerUniqueName, int pointsScored)
    {
        if (Contestants == null)
        {
            Contestants = new List&lt;Player&gt;();
        }

        Player player = Contestants.FirstOrDefault(c =&gt;
            c.UniqueName == playerUniqueName &amp;&amp;
            c.SportName == this.GetType().Name);

        if (player == null)
        {
            player = new Player();
            player.UniqueName = playerUniqueName;
            player.SportName = this.GetType().Name;
            player.Score = $"{pointsScored} {(pointsScored &gt; 1 ? " points" : " point")}";
            player.AchievedDate = DateTime.Now;

            Contestants.Add(player);
        }
        else
        {
            int scoreValue = Convert.ToInt32(player.Score.Substring(0, player.Score.IndexOf(" ")));
            scoreValue = scoreValue + pointsScored;
            player.Score = $"{scoreValue} points";
        }
    }
}

SportResults class:

public class SportResults
{
    public SportResults()
    {
        Contestants = new List&amp;lt;Player&amp;gt;();

        FillHockeyResults();
        FillBaseballResults();
        FillFootballResults();
        FillBasketballResults();
    }

    public List&amp;lt;Player&amp;gt; Contestants { get; set; }

    public Hockey Hockey { get; set; }

    public Baseball Baseball { get; set; }

    public Football Football { get; set; }

    public Basketball Basketball { get; set; }

    public void FillHockeyResults()
    {
        Random rnd = new Random(DateTime.Now.Millisecond);

        Hockey = new Hockey();
        Hockey.Contestants = Contestants;

        for (int i = 0; i &lt; rnd.Next(); i++)
        {
            Hockey.AddScore("Albert");
        }

        for(int i = 0; i &lt; rnd.Next(); i++)
        {
            Hockey.AddScore("Beau");
        }

        for (int i = 0; i &lt; rnd.Next(); i++)
        {
            Hockey.AddScore("Charles");
        }

        for (int i = 0; i &lt; rnd.Next(); i++)
        {
            Hockey.AddScore("David");
        }

        for (int i = 0; i &lt; rnd.Next(); i++)
        {
            Hockey.AddScore("Eugene");
        }
    }

    public void FillBaseballResults()
    {
        Random rnd = new Random(DateTime.Now.Millisecond);

        Baseball = new Baseball();
        Baseball.Contestants = Contestants;

        for (int i = 0; i &lt; rnd.Next(); i++)
        {
            Baseball.AddScore("Beau");
        }

        for (int i = 0; i &lt; rnd.Next(); i++)
        {
            Baseball.AddScore("Charles");
        }

        for (int i = 0; i &lt; rnd.Next(); i++)
        {
            Baseball.AddScore("David");
        }

        for (int i = 0; i &lt; rnd.Next(); i++)
        {
            Baseball.AddScore("Eugene");
        }

        for (int i = 0; i &lt; rnd.Next(); i++)
        {
            Baseball.AddScore("Farley");
        }
    }

    public void FillFootballResults()
    {
        Random rndTimesScored = new Random(DateTime.Now.Millisecond);
        Random rndPointsScored = new Random(DateTime.Now.Millisecond);

        Football = new Football();
        Football.Contestants = Contestants;

        for (int i = 0; i &lt; rndTimesScored.Next(); i++)
        {
            Football.AddScore("Charles", rndPointsScored.Next(1, 7));
        }

        for (int i = 0; i &lt; rndTimesScored.Next(); i++)
        {
            Football.AddScore("David", rndPointsScored.Next(1, 7));
        }

        for (int i = 0; i &lt; rndTimesScored.Next(); i++)
        {
            Football.AddScore("Eugene", rndPointsScored.Next(1, 7));
        }

        for (int i = 0; i &lt; rndTimesScored.Next(); i++)
        {
            Football.AddScore("Farley", rndPointsScored.Next(1, 7));
        }

        for (int i = 0; i &lt; rndTimesScored.Next(); i++)
        {
            Football.AddScore("Godrick", rndPointsScored.Next(1, 7));
        }
    }

    public void FillBasketballResults()
    {
        Random rndTimesScored = new Random(DateTime.Now.Millisecond);
        Random rndPointsScored = new Random(DateTime.Now.Millisecond);

        Basketball = new Basketball();
        Basketball.Contestants = Contestants;

        for (int i = 0; i &lt; rndTimesScored.Next(); i++)
        {
            Basketball.AddScore("David", rndPointsScored.Next(1, 3));
        }

        for (int i = 0; i &lt; rndTimesScored.Next(); i++)
        {
            Basketball.AddScore("Eugene", rndPointsScored.Next(1, 3));
        }

        for (int i = 0; i &lt; rndTimesScored.Next(); i++)
        {
            Basketball.AddScore("Farley", rndPointsScored.Next(1, 3));
        }

        for (int i = 0; i &lt; rndTimesScored.Next(); i++)
        {
            Basketball.AddScore("Godrick", rndPointsScored.Next(1, 3));
        }

        for (int i = 0; i &lt; rndTimesScored.Next(); i++)
        {
            Basketball.AddScore("Harry", rndPointsScored.Next(1, 3));
        }
    }
}

There…if you use the above code…all you have to do is instantiate a new SportResults object and you’ll have a bunch of ficticious data with some relational data between the players.  Now to display that data.  I whipped up a pretty basic WPF app with a RadTabControl and some RadGridViews to display the data.  Here’s what my MainWindow.xaml \ cs files look like as well as the custom user control I created to display results for reach sport.

SportResultsUserControl XAML:

<UserControl    d:DesignHeight="300" 
                d:DesignWidth="300" 
                mc:Ignorable="d" 
                x:Class="ToolTipinator.PlayerStatsUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                xmlns:local="clr-namespace:ToolTipinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <telerik:RadGridView    AutoGenerateColumns="False" 
                            ColumnWidth="*" 
                            x:Name="PlayerStatsRadGridView">
        <telerik:RadGridView.Columns>
            <telerik:GridViewDataColumn DataMemberBinding="{Binding SportName}" 
                                        Header="Sport" />
            <telerik:GridViewDataColumn DataMemberBinding="{Binding Score}" 
                                        Header="Score" />
            <telerik:GridViewDataColumn DataMemberBinding="{Binding AchievedDate}" 
                                        Header="Achieved Date" />
        </telerik:RadGridView.Columns>
    </telerik:RadGridView>
</UserControl>

SportResultsUserControl CS:

public partial class SportResultsUserControl : UserControl
{
    public SportResultsUserControl(IEnumerable&amp;lt;Player&amp;gt; contestants)
    {
        InitializeComponent();

        SportResultsRadGridView.ItemsSource = contestants;
    }
}

MainWindow XAML:

<Window Height="350" 
        Width="525" 
        Title="MainWindow" 
        x:Class="ToolTipinator.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <telerik:RadTabControl  Margin="5, 5, 5, 5" 
                                x:Name="ToolTipinatorTabControl">
            <telerik:RadTabItem BorderBrush="Black" 
                                BorderThickness="1" 
                                Header="Hockey" 
                                x:Name="HockeyRadTabItem" />
            <telerik:RadTabItem BorderBrush="Black" 
                                BorderThickness="1" 
                                Header="Baseball" 
                                x:Name="BaseballRadTabItem" />
            <telerik:RadTabItem BorderBrush="Black" 
                                BorderThickness="1" 
                                Header="Football" 
                                x:Name="FootballRadTabItem" />
            <telerik:RadTabItem BorderBrush="Black" 
                                BorderThickness="1" 
                                Header="Basketball" 
                                x:Name="BasketballRadTabItem" />
        </telerik:RadTabControl>
    </Grid>
</Window>

MainWindow CS:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        SportResults = new SportResults();

        ((RadTabItem)ToolTipinatorTabControl.Items[0]).Content = new SportResultsUserControl(SportResults.Hockey.GetResults());
        ((RadTabItem)ToolTipinatorTabControl.Items[1]).Content = new SportResultsUserControl(SportResults.Baseball.GetResults());
        ((RadTabItem)ToolTipinatorTabControl.Items[2]).Content = new SportResultsUserControl(SportResults.Football.GetResults());
        ((RadTabItem)ToolTipinatorTabControl.Items[3]).Content = new SportResultsUserControl(SportResults.Basketball.GetResults());
    }

    public SportResults SportResults { get; set; }
}

Ok…now we should all have a spiffy little WPF app that has 4 tabs one for each sport of Hockey, Baseball, Football, and Basketball.

You may notice that David and Eugene have played in each sport…and some other’s have played in 2 or 3 different sports while some have only played in a single sport.  That’s where the relational data comes in and what I’ll be using in my ToolTip.

What I’m going to do next is set it up so when you hover over a player’s name, you’ll get a ToolTip popup that will display their scores in other sports.  Pretty short and sweet, but it gives a good example of how to set it up.  To do this I’m going to make a custom user control just for the ToolTip.

PlayerStatsUserControl XAML:

<UserControl    d:DesignHeight="300" 
                d:DesignWidth="300" 
                mc:Ignorable="d" 
                x:Class="ToolTipinator.PlayerStatsUserControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                xmlns:local="clr-namespace:ToolTipinator"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <telerik:RadGridView    AutoGenerateColumns="False" 
                            ColumnWidth="*" 
                            x:Name="PlayerStatsRadGridView">
        <telerik:RadGridView.Columns>
            <telerik:GridViewDataColumn DataMemberBinding="{Binding SportName}" 
                                        Header="Sport" />
            <telerik:GridViewDataColumn DataMemberBinding="{Binding Score}" 
                                        Header="Score" />
            <telerik:GridViewDataColumn DataMemberBinding="{Binding AchievedDate}" 
                                        Header="Achieved Date" />
        </telerik:RadGridView.Columns>
    </telerik:RadGridView>
</UserControl>

PlayerStatsUserControl CS:

public partial class PlayerStatsUserControl : UserControl
{
    public PlayerStatsUserControl(IEnumerable&amp;lt;Player&amp;gt; contestantResults)
    {
        InitializeComponent();

        PlayerStatsRadGridView.ItemsSource = contestantResults;
    }
}

Nothing overly complex.  The magic happens in a new event handler that will need to be added to the SportResultsUserControl for RowLoaded.  I also added an argument to the constructor to set the sport name for the SportResults object.  Here’s what my CS for my SportResultsUserControl looks like now.

SportResultsRadGridView CS:

public partial class SportResultsUserControl : UserControl
{
    public SportResultsUserControl(string sportName, IEnumerable&amp;lt;Player&amp;gt; contestants)
    {
        InitializeComponent();

        if (DesignerProperties.GetIsInDesignMode(this))
            return;

        SportName = sportName;
        SportResultsRadGridView.ItemsSource = contestants;
    }

    public string SportName { get; set; }

    private void SportResultsRadGridView_RowLoaded(object sender, RowLoadedEventArgs e)
    {
        GridViewRow currentRow = null;

        if (e.Row.GetType() == typeof(GridViewRow))
        {
            currentRow = (GridViewRow)e.Row;
        }

        if (currentRow != null)
        {
            var nameCell = currentRow.Cells.FirstOrDefault(c =&gt; c.Column.UniqueName == "UniqueName");

            if (nameCell != null)
            {
                string cellValue = ((GridViewCell)nameCell).Value.ToString();

                if (cellValue != null)
                {
                    IEnumerable&lt;Player&gt; contestantResults = MainWindow.SportResults.Contestants.Where(c =&gt; c.UniqueName == cellValue &amp;&amp; c.SportName != SportName);
                    PlayerStatsUserControl playerStatsToolTip = new PlayerStatsUserControl(contestantResults);
                    playerStatsToolTip.Width = 800;
                    nameCell.ToolTip = playerStatsToolTip;
                }
            }
        }
    }
}

In order for the above to work, I also had to turn the SportResults property that was a memeber of MainWindow into a static property so I could access it elsewhere as well as adding in the sport names to the constructor calls for the SportResultsUserControl…again, here’s what the CS for my MainWindow looks like now…

MainWindow CS:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        ToolTipService.ShowDurationProperty.OverrideMetadata(
            typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));

        MainWindow.SportResults = new SportResults();

        ((RadTabItem)ToolTipinatorTabControl.Items[0]).Content = new SportResultsUserControl(SportResults.Hockey.GetType().Name, SportResults.Hockey.GetResults());
        ((RadTabItem)ToolTipinatorTabControl.Items[1]).Content = new SportResultsUserControl(SportResults.Baseball.GetType().Name, SportResults.Baseball.GetResults());
        ((RadTabItem)ToolTipinatorTabControl.Items[2]).Content = new SportResultsUserControl(SportResults.Football.GetType().Name, SportResults.Football.GetResults());
        ((RadTabItem)ToolTipinatorTabControl.Items[3]).Content = new SportResultsUserControl(SportResults.Basketball.GetType().Name, SportResults.Basketball.GetResults());
    }

    public static SportResults SportResults { get; set; }
}

You may notice the call to ToolTipService in the MainWindow constructor as well now.  This will cause the ToolTip to stay open for about 47 days…so it’s not forever, but I’m pretty sure if a user cannot get the info they need out of a ToolTip within 47 days you have bigger problems with your app.  😉

Ok…that’s it for today’s journey.  Thanks for reading and as always I hope this helps someone.  Enjoy!  🙂

Setting a Custom WPF ToolTip and how to keep it shown…