Evolvinator

Hey all.

Yup…that’s all I got after having taken a pretty long haitus from writing stuff in this blog. ¬†For a while I turned kinda health nut and lost a bunch of weight…and blogged about that stuff for a while, but that’s not what I really want to be spending my time thinking and blogging about. ¬†I think what happened is I was finding ways to keep my brain interested in the process of eating better and exercising…because I hated it. ¬†I’m still doing it…I still hate it…but it’s more a part of my everyday life now. ¬†That’s how it’s gotta be if you really want to keep healthy and keep weight off…but like I said, it’s not something I’m terribly intersted in so I’m not going to actively blog about unless I have something I to say that’s burning it’s way through my fingers.

Ok, if you’re still with me…great. ¬†This page is a landing spot for the project I kicked off with my younger kids. ¬†The concept is that I’m going to use this as a way to show them how the coding process works. ¬†Starting a project out in source control…designing the concept of the program and how objects should interact, coding up a framework to build from, reviewing code before it’s checked in, etc… ¬†So far they seem to love it…unfortunately I’m a terrible teacher so they’ll have that to keep dealing with.

So the basic idea I pitched to the boys was that Evolvinator was going to be an app that has the concept of a cycle. ¬†At the start of every cycle it creates a “Toffoid”. ¬†The toffoid currently just exists and sits in memory…and every new cycle another is created and added to the collection. ¬†I know…stupid…but a simple concept for an 8 and 10 year old to get their heads wrapped around. ¬†I then said my intent is to alter the “toffoid” code so that they would start interacting…and then eventualy mutate on their own. ¬†They were hooked.

One of them immediately started coming up with different varients of toffoids and started making artwork for them. ¬†The other started getting really into the coding side of things and started writing his own programs to learn what my code was doing. ¬†I’m so excited…they are showing interest in what I do for a living…well sorta. ¬†Now I just have to figure out how to keep fostering that curiosity and not let it die. ¬†If I can do that, they’ll have a much better jumping off point in the coding world than I did. ¬†I always said I wish I would have gone to school for programming instead of being self taught. ¬†There so much training in logic and patterns than a person gets while going through all the education in coding techniques that took me many years to figure out on my own. ¬†It would be so awesome if I could give them an edge by teaching them what I know.

I’m using Microsoft accounts and Visual Studio Online for an IDE and source control. ¬†I’m still trying to convert to the world of “git” myself so it’ll be good for me as well.

I’ll be posting code source and details about design as I go forward as well as any interesting conversations the boys and I have that I think is worth chatting about, but this is the kick off. ¬†Enjoy! ¬†ūüôā

Update 01-05-2017:

I’ve got the framework of the app down. ¬†I have given it the concept of a cycle which is currently 10 seconds. ¬†Every cycle it creates a “Toffoid” object.

A “Toffoid” currently consists of having a Guid for an Id and a value of 1-10 for LifePoints. ¬†They are then added to a List<Toffoid> collection and that’s where they sit.

I made the Toffoid serializable and when the program exits it serializes created Toffoids out to a binary file…when the app starts it deserializes them back into the List<Toffoid> in memory.

That’s about all the further I’ve take it so far as I’m waiting for the kiddos to do some code review. ¬†Idea’s we’ve talked about is having different classes derive from Toffoid to make things like a planet for other Toffoids to exist on, evolved Toffoids, non-evolving Toffoids to give the evolving ones something to stress about and give them the need to evolve. ¬†Idea’s about land, flying, digging, and swimming variants of Toffoids have been talked about.

The term “Habaltar” has been given to Toffoids that can evolve into other things. ¬†That’s it for now. ¬†Possibly some artwork from the kiddos about what these things could look like if we decide to add graphics to the project. ¬†ūüôā

Update 02-08-2017:

Now that the basic idea has been figured out…I decided that I really must have some way to display the stuff that’s floating around in memory…and then I decided I’m going to take that a step further and actually figure out how the Toffoids are going to exist…so Ploffoids came to be. ¬†A ploffoid is a toffoid that other toffoid’s can live on…now how to show ploffoids.

I felt that it would be easiest to try to pick up Unity and see if I can make the graphics part work using that…to that end I went through the “Roll a Ball” tutorial and managed to create my first game with it…now I’m just trying to find a way to create a cuboid type globe…which will be my “ploffoids”. ¬†I’ll post some more info once I figure that stuff out. ¬†ūüôā

Here’s a couple of posts I’ve come across that talk about something similar to what I’m trying to do.

http://entitycrisis.blogspot.com/2011/02/uniform-points-on-sphere.html

http://answers.unity3d.com/questions/142630/sphere-made-of-cubes-algorithm.html

http://answers.unity3d.com/questions/257815/how-do-i-generate-cubes-to-form-a-sphere.html

Evolvinator

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

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…

From the Begining – Object Oriented Design

Hey all. ¬†Life has gotten really crazy as I’ve increased my workout schedule and been plagued with DLC for Fallout 4 so my blogging is suffering, but I’m not going to just let it end as I have more to type…and now I may even have an interested audience…someone who’s specifically asking me advice about how to get into coding.

He specifically said he just wanted to learn C#…which is something I used to say as well way back when…however what I’ve learned since then that C# more or less the adhesive that bind things together. ¬†It’s really .NET and what it can do that is the real meat of C#. ¬†And once you realize that…you find that you can use a whole bunch of different languages to do the same base .NET things and that it’s not only just C#.

What I also learned is that C# is a comfort zone…but one a person seriously wanting to learn how to code needs to break out of. ¬†Sure if you become a C# wiz, you’ll be able to slap together some cool slick object models…make some nifty libraries out of them…etc…however if you want to display anything to a user…these days you pretty much will have to know at least some minimal level of HTML / XAML. ¬†Then if you go the HTML route you’ll probably also need a little javascript / jquery. ¬†You can’t paint a modern UI picture with just C# anymore…WinForms are dying and WPF / MVC is here to stay…which is a good thing. ¬†Then if you want to store that data…sure you can write a file out to the local storage…but someone serious will want to work with a database so there will inevitably be some SQL showing up in your future…anyway, a long paragraph short, don’t be too locked down to C#…it’s a good jumping off point, but eventually you’ll need to branch out. ¬†It’s just how it is so get it in your head now.

So first let put this out there right off…I learned everything I know about C# and .NET from reading books and the internet. ¬†One of the first books I read that was extremely useful was called “Beginning C# Game Programming. ¬†Even though it’s very game centric…it does an amazing job at explaining key elements of C# / .NET and how they apply to object design in a very easy to understand way. ¬†I often look back to the chapter on polymorphism to this day.

So trying to not just plaguarize the hell out of that book…I’ll just be giving pointers as a brain dump sort of style in these “From the Begining” blog posts.

The big strength of C# is its ability to define an object. ¬†Everything in the universe boils down to being something that’s definable…C# gives you the structure to put that definition into something tangable. ¬†Very basically and simply put…these tools for structure are constructors, properties, and methods.

One of the tricks I learned early on was to take the object you’re trying to define and write a sentence out about what you’re trying to do with it.

I’m trying to count all the stars in the solar system that have planets.

Nouns are your object names and properties, verbs are your methods.

So we have “Solar System” which would be the object name. ¬†Then we have “Stars” which would be a property hanging off Solar System…which in turn has “Planets” which hangs as a property for a Star. ¬†Already we’re starting to see some structure in our definition. ¬†The tricky part is that we’re trying to count the stars that have planets…which means that the Stars object will need a “Count” method. ¬†It just so happens when you use a generic collection from .NET you get¬†a¬†Count for free (it’s a little misleading in this case because it’s a property and not a method…Count should really be a method in my way of thinking but that’s a discussion for another time). ¬†So here’s what the code would look like from the short little sentence above.

Planet Object:

public class Planet
{
    public string Name { get; set; }
}

Star Object:

public class Star
{
    public List<Planet> Planets { get; set; }
}

SolarSystem Object:

public class SolarSystem
{
    public List<Star> Stars { get; set; }
}

Really that’s all you “have” to do to get the info you need to satisfy the definition sentence. ¬†You spin through the collection of “Stars” and check the Count property of each Star object’s “Planets” property. ¬†It would look something like this…

SolarSystem milkyWay = new SolarSystem();
int starsWithPlanetsCount = 0;

foreach (Star star in milkyWay.Stars)
{
    if(star.Planets.Count > 0)
    {
        starsWithPlanetsCount++;
    }
}

…it works but lets add a couple other properties¬†to make things a little easier…so now our SolarSystem object code looks like the following…

public class SolarSystem
{
    public List<Star> Stars { get; set; }

    public List<Star> StarsWithPlanets
    {
        get
        {
            List<Star> returnList = new List<Star>();

            foreach(Star star in Stars)
            {
                if(star.Planets.Count > 0)
                {
                    returnList.Add(star);
                }
            }

            return returnList;
        }
    }

    public List<Star> StarsWithoutPlanets
    {
        get
        {
            List<Star> returnList = new List<Star>();

            foreach (Star star in Stars)
            {
                if (star.Planets.Count <= 0)
                {
                    returnList.Add(star);
                }
            }

            return returnList;
        }
    }
}

…and the calling code would look something like this…

SolarSystem milkyWay = new SolarSystem();
int starsWithPlanetsCount = milkyWay.StarsWithPlanets.Count;

So much easier and clearner. Personally I always like to keep the logic and work being done on a specific object within a method or property in that object instead of having it in calling code. ¬†It’s just a way to logically group code and make it more organized…but it’s more a personal preference thing I think.

Anyway, that’s my first lesson about C#. Use it for what it’s good for…which is defining objects. Hope this helps someone. Enjoy! ūüôā

From the Begining – Object Oriented Design