Skip to content

Maintaining C# Class Library Models With Python

When it comes to a scripting language, nothing can beat Python. It portable, meaning it can run on any platform. It’s an interpreted language so the code doesn’t always have to re-compiled whenever you make changes to a script. It can easily perform all the important system or executable calls you could do with a regular bash script with far more readable syntax. Most importantly, it’s super accessible. No opening up Visual Studio or another code editor to create a project before you can even start coding. All you need is a .py file with some statements, and point Python to it in the terminal to run it. Often times, you can whip up whatever logic you need in a terminal editor and be running the script before you even got a project created in another language.

Python really shines on a lot of things but I specifically rely on it for simple file manipulation during my day job. So much that I put Python scripts in my C# projects just to maintain rather large code bases. If I need a certain quick task performed to verify certain parts of my code are correct, I can open up a terminal and fire off the task without even needing to open up another editor.

So what could I use it for? Pretty much anything!

Test case

I am currently working on a project for a client that involves a C# .Netstandard Class Library, a UWP client application and a back end .NET ASP Web API server. The class library holds over for 40 large classes ranging from having 30 to 100 properties each. These models represent the database via Entity Framework and SQL Server as well as the classes for the client application views. Everything needs to be right with these classes as a lot is relying on them.

Please note the following example has been sanitized for this demonstration.

The class library models

As you can see from the following class, each class file includes the copyright header, the class as well as tens of properties of varying datatypes.

/*
	__ _/| _/. _  ._/__ /
	_\/_// /_///_// / /_|/
		   _/
	copyright (c) sof digital 2020
	all rights reserved | under reserved 
	proprietary & confidential
	no unauthorized file duplication without consent
	written by michael rinderle <michael|at|sofdigital|dot|net>
*/

namespace SampleApp.Library.Models.Entity
{
    public class SampleClassOne : BaseSampleClass
    {
        public SampleClassOne() { }

        public bool? Property1 { get; set; }
        public string Property2 { get; set; }
        public bool? Property3 { get; set; }
        // note: continuation
        public decimal? Property49 { get; set; }
        public string Property50 { get; set; }
    }
}

In the UWP application, custom user controls have been created for the user interface. This control will contain a header for the label, an input field, and a label will also display a validation status if it has not been inputted when a validation event has been triggered.

<controls:FrmTextBox
    Header="Property 1"
    Text="{x:Bind ViewModel.SampleClassOne.Property, Mode=TwoWay}"
    InspectionValidation="{x:Bind ViewModel.ErrorValidation.Property, Mode=TwoWay}">
</controls:FrmTextBox

For validation, I like to use the Nuget package, FluentValidation. What is nice about the package is it allows you to make rules in a fluent way and attach those rules to a model like so. Here I am creating a validator with our previous class. I can now apply a rule against Property50 which is a string datatype the requirement of a thousand characters maximum so it fits into our database correctly once it is submitted. It it doesn’t meet the requirement, the message “Maximum Characters [1000] will return for the requirement message.

using FluentValidation;
using SamleApp.Library.Models.Entity;

namespace SamleApp.Library.Validation.Validators
{
    public class SampleClassOneValidator : AbstractValidator<SampleClassOne>
    {
        public SampleClassOneValidator()
        {
            RuleFor(class => class.Property50).MaximumLength(1000).WithMessage("Maximum Characters [1000]");
        }
    }
}

Now you can instantiate the validator, validate a new object of SampleClassOne and get the results. That is all well and good but as we iterate over the errors, I don’t have a way of storing the validation error message yet to alert the user. To fix this, I need to make a new class with the same property names but with all string datatypes so we can use reflection and the errors’ PropertyName to match with the new error class properties and dump the error message into that variable that is binding to our UI validation label.



var validator = new SampleClassOneValidator();
var errors = validator.Validate(SampleClassOne);
if (errors.IsValid) return true;

foreach (var error in errors.Errors)
{
    ErrorValidation.GetType()
        ?.GetProperty(error.PropertyName)
        ?.SetValue(ErrorValidation, error.ErrorMessage, null);
}

So with roughly 40 classes, that just turned into 80 plus classes just to get a validation feature. What is worse is this project is in heavy active development. The error result classes reflect the regular classes so if a change is made in the regular class it needs to be changed in the error result classes.

When making a change and I miss updating the corresponding error result class, that could mean incorporating a bug into the program. If 40 original classes have on average 50 properties to them, that means 2000 properties. If we count the error result classes that have properties that all need to be string datatypes, we have over 4000 properties to keep track of. At a certain point in the development of this project, I didn’t want to do that anymore so I made a simple script in less than 10 minutes to write and debug to fix this problem.

The following script looks for my defined classes in my Entity Framework model folder. It will dissect the whole class looping over each property in the class. When it finds a property with the datatypes int?, bool?, or decimal?, it will replace that with a string datatype. After that it will create a new error result class with the corresponding name, import my copyright header, and then re-create the class in the all string datatype format with the same property names.

import os

banner = '''
/*
	 __ _/| _/. _  ._/__ /
	_\/_// /_///_// / /_|/
	           _/
	copyright (c) sof digital 2020
	all rights reserved | under reserved 
	proprietary & confidential
	no unauthorized file duplication without consent
	written by michael rinderle <michael|at|sofdigital|dot|net>
*/
'''
# Form types
class_types = [   
    "SampleClassOne.cs",
    "SampleClassTwo.cs",
    "SampleClassThree.cs",
    "SampleClassFour.cs",
    "SampleClassFive.cs",]

# Path locations
entity_location = r"C:\SampleApp\SampleApp.Library\Models\Entity"
result_location = r"C:\SampleApp\SampleApp.Library\Models\ErrorResults"

for cls in class_types :
   
    # Create path locations for reading and saving
    path = os.path.join(entity_location, cls )
    save_path = os.path.join(result_location, form.replace(".cs", "ErrorResults.cs"))
    property_list = list()
    
    # look for replacements in entity file
    with open(path, "r") as f:
        for line in f:
            if 'bool?' in line:
                property_list.append(line.replace("bool?", "string"))
            if 'decimal?' in line:
                property_list.append(line.replace("decimal?", "string"))
            if 'int?' in line:
                property_list.append(line.replace("int?", "string"))
            if 'string' in line:
                property_list.append(line)

    # create error result class 
    with open(save_path, "w+") as f:
        f.write(banner + "\n")
        f.write("namespace SampleApp.Library.Models.ErrorResults\n")
        f.write("{\n")
        f.write("\tpublic class {}ErrorResults\n".format(form.replace(".cs", "")))
        f.write("\t{\n")
        for prop in property_list:
            f.write(prop)
        f.write("\t}\n")
        f.write("}")

After it iterates over 40 plus class files, we get our result for each class. A new error result class type that refects our regular classes but now has only string datatypes. If the Property50 property is over 1000 characters, the error string will be input into that property and reflected in the UI upon an invalidation.

/*
	__ _/| _/. _  ._/__ /
	_\/_// /_///_// / /_|/
		   _/
	copyright (c) sof digital 2020
	all rights reserved | under reserved 
	proprietary & confidential
	no unauthorized file duplication without consent
	written by michael rinderle <michael|at|sofdigital|dot|net>
*/

namespace SampleApp.Library.Models.Entity
{
    public class SampleClassOneErrorResults 
    {
        public string  Property1 { get; set; }
        public string Property2 { get; set; }
        public string  Property3 { get; set; }
        // note: continuation
        public string  Property49 { get; set; }
        public string Property50 { get; set; }
    }
}

Conclusion

I can never stress enough the power of the Python scripting language. In one simple script, I can verify over 4000 class properties are correct in less than a few seconds of running it. I don’t have to manually manipulate 80 files. No need to create a new project, I don’t even need to open up an editor. I could do all this straight from a terminal while I am working alongside my C# projects in Visual Studio. Before a release I can now run all my python scripts to make sure each part of the code base is absolutely correct before shipping. Whether you are a programmer or not, I highly recommend picking up the language and I hope this enlightened you on what you can do with Python in your day to day operations.

Leave a Reply

Your email address will not be published. Required fields are marked *