Extending

Overview

SeedPacket has been created from the beginning for extensibility. The logic for data generation is contained in Rules that can be easily removed, replaced, or expanded. Individual rules have access to a plethora of functionality that is passed in via dependency injection. The Generators are passed in as interfaces that can be easily switched out with new implementations, as can the DataSources, and all the source data that popluates the DataSources (in either an XML/JSON file or string).

We are going to focus on two extensibility points, Custom Extension Methods and Custom Generators

Custom Extension Methods

This example shows a simple yet extremely powerful way to customize data generation for a whole project. The extension method overloads in the SeedPacket.Extensions for the main .Seed() methods have purposely been separated from the actual implementations in the SeedCore class in the root SeedPacket namespace. This means you can easily create your own custom implementation of .Seed() that just works everywhere in you project with your own Rules, defaults and data!

The actual code on this page is trivial as it a streamlined version of the SeedPacket.Extensions that you can find in the SeedPacket source code and is a good starting point for your own custom extensions with but with defaults set to your own liking. It simplifies and removes some of the overloads from the source code version, and sets the source data to come from our own XML file. Once you have your ideal solution, you will find it generic enough that you will be able to take it to other projects to use for prototyping.

One thing to note is that the namespace Examples.Extensions is imported into the page, but the SeedPacket.Extensions namespace is NOT. This allows our custom version(s) of .Seed() to work without namespace collisions. Another is that we are using a CustomGenerator that inherits from MultiGenerator. We will look at this a little later on the page.

To Run Code:

Create a folder called Extensions and copy the code below into a file called CustomSeedExtensions.cs using the name of your own namespace in place of {YourNamespace}.

Create a folder called SourceFiles. Download the file XmlSeedSourcePlus.xml and save it into this folder.

Title Album Artist Released
1 Butterfly Blues The Asking Sea Lion 12/9/2021
2 Extemporaneous Quartet Rap Crew 7/12/2021
3 Only One Know Your Friends Hurt No More 11/22/2021
4 The Man and the Stars Gorilla Joint Fiddly Bits 12/23/2023
5 Troubles Will Come Fresh Trixy OldHouse 4/5/2021
6 Groovy Tunes Thanks for the Fish Temper Flare 1/25/2024
7 You Will Find Love Egg in the Eye Jon ColdRidge 10/12/2023
8 Take Your Time Fresh Mac Truck 6/17/2021
9 A Brighter Shade of Gray Fresh Temper Flare 1/22/2023
10 The Stream The Asking Audacious Criminal 6/4/2024
11 Seredipity2 Hope for the Masses The Bad Ones 6/6/2022
12 A Brighter Shade of Gray Fresh Like More Water 7/15/2022
13 Take Your Time Freshly Rejected Boodle Jones 7/8/2022
14 Don't Worry Partial Mission Dylan Dylan 10/25/2023
15 A Brighter Shade of Gray Gangland Frame Boodle Jones 10/12/2022
Import the main SeedPacket namespace and not the SeedPacket.Extensions to avoid conflicts with your own new custom extension methods.
using SeedPacket; using SeedPacket.Functions; using SeedPacket.Generators; using System; using System.Collections.Generic; using System.Linq; using static SeedPacket.Examples.Helpers.Common; namespace {YourNamespace}.Extensions { // See source of SeedPacket.Examples/Logic/Extensions/CustomSeedExtensions.cs for code. public static class CustomSeedExtensions { private static int defaultSeed = 34567; private static readonly string sourcePath = $@"{GetApplicationRoot()}\Logic\SourceFiles\xmlSeedSourcePlus.xml"; // Simplified and streamlined version of SeedPacket.Seed() extension public static List<T> Seed<T>( this IEnumerable<T> iEnumerable, int? seedEnd = null, int? seedBegin = null, int? randomSeed = null, string customPropertyName = null ) { var gen = CustomGenerator(sourcePath, seedEnd, seedBegin, randomSeed, customPropertyName); return new SeedCore(gen).SeedList(iEnumerable).ToList(); } // Remove and simplify if you are not going to use need a Dictionary implementation public static IDictionary<TKey, TValue> Seed<TKey, TValue> ( this IDictionary<TKey, TValue> iDictionary, int? seedEnd = null, int? seedBegin = null, int? randomSeed = null, string customPropertyName = null ) { var gen = CustomGenerator(sourcePath, seedEnd, seedBegin, randomSeed, customPropertyName); return new SeedCore(gen).SeedList(iDictionary); } // Set common defaults here private static CustomGenerator CustomGenerator( string xmlSourcePath, int? seedEnd, int? seedBegin, int? randomSeed, string customPropertyName) { return new CustomGenerator(xmlSourcePath, dataInputType: DataInputType.XmlFile) { SeedBegin = seedBegin ?? 1, SeedEnd = seedEnd ?? 10, BaseRandom = new Random(randomSeed ?? defaultSeed), BaseDateTime = DateTime.Now, CustomName = customPropertyName }; } } }

Custom Generators

Another extensibility point is demonstrated in the code below by building a custom IGenerator. The CustomGenerator inherits all functionality from the MultiGenerator and adds few new features. In this case, it adds a RulesSet.Advanced option as the default and adds some additional rules, including a Rule that fills the CEO property. Of course these could be more extensive, including a completely new default Rules implementation, etc.

The GetRules() method of the MultiGenerator is declared as virtual so that it can be overriden by a derived class. This method is called by the constructor to load its Rules collection based on the RulesSet enum that it is passed. So be overriding it, you can redefine which Rules are loaded for each enum. In fact, 3 values in the enum, Advanced, UnitTest, and Custom do not have an implementation are there purely for customization in derived classes.

To Run Code:

Create a folder called Generators and copy the code above into a file called CustomGenerator.cs using the name of your own namespace in place of {YourNamespace}.

To use in the CustomSeedExtensions above, replace all references to the MultiGenerator class with a reference to the new CustomGenerator in {YourNamespace}.

CEO Company City State
John Smith City Inc Winterspring CA
Patricia Johnson Capital Pinetown RI
Michael Williams Acme LLC Gotham AL
Susan Jones Tyrell Ind Brighton AR
William Brown Oscorp Co Austin KS
Mary Davis Virtucon Co Eastdale NM
Robert Miller More Corporation Pinetown SC
Sarah Wilson Oscorp Co Metropolis SC
Peter Moore Stark Co Oakleaf TX
Ann Taylor Umbrella Inc Stone Bridge NH
Roger Anderson Sunrise LLC Westmoor GA
Theresa Thomas Stark Co Westmoor NH
Replace {YourNamespace} with your own namespace.
using SeedPacket; using SeedPacket.Functions; using SeedPacket.Generators; using System; using System.Collections.Generic; namespace {YourNamespace}.Generators { public class CustomGenerator : MultiGenerator { public CustomGenerator( string sourceFilepath = null, string sourceString = null, DataInputType dataInputType = DataInputType.Auto, RulesSet rulesSet = RulesSet.Advanced ) : base(sourceFilepath, sourceString, dataInputType, rulesSet) { } // Can create custom sets of Rules protected override void GetRules(RulesSet ruleSet) { switch (ruleSet) { case RulesSet.None: // No rules loaded. Add rules manually break; case RulesSet.Basic: Rules.AddBasicRules(); break; case RulesSet.Common: Rules.AddBasicRules(); Rules.AddCommonRules(); break; case RulesSet.Advanced: // Used by default Rules.AddBasicRules(); Rules.AddCommonRules(); AddAdvancedRules(Rules); break; case RulesSet.UnitTest: Rules.AddBasicRules(); // Can Add or change Rules here break; case RulesSet.Custom: Rules.AddBasicRules(); Rules.AddCommonRules(); // Can Add or change Rules here break; default: throw new NotImplementedException("That is not a valid RulesSet."); } } // Example. Obviously this could be more extensive... public static void AddAdvancedRules(IRules rules, bool overwrite = true) { var advanceRules = new List { new Rule(typeof(DateTime), "Create%", g => g.BaseDateTime.AddDays(g.RowRandom.Next(-30, 1)), "DateTimeInLastMonth"), new Rule(typeof(string), "Description%", g => g.GetElementRandom("Description"), "Description", "Gets Description from custom XML file"), new Rule(typeof(string), "Ceo", g => GetCeoName(g), "Random CEO Name"), }; rules.AddRange(advanceRules, overwrite); } // Can break out rules into separate methods for easier debugging or if Rule is complex. private static dynamic GetCeoName(IGenerator g) { return $"{g.GetElementNext("FirstName")} {g.GetElementNext("LastName")}"; } } }