Extending

Overview

SeedPacket has been created from the beginning for extensibility. After all, 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

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; namespace {YourNamespace}.Extensions { public static class CustomSeedExtensions { private static int defaultSeed = 3456; // 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 propertyName = null ) { var gen = new MultiGenerator("~/SourceFiles/xmlSeedSourcePlus.xml", dataInputType: DataInputType.XmlFile) { SeedBegin = seedBegin ?? 1, SeedEnd = seedEnd ?? 10, BaseRandom = new Random(randomSeed ?? defaultSeed), BaseDateTime = DateTime.Now, CurrentPropertyName = propertyName }; 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 propertyName = null ) { var gen = new MultiGenerator("~/SourceFiles/xmlSeedSourcePlus.xml", dataInputType: DataInputType.XmlFile) { SeedBegin = seedBegin ?? 1, SeedEnd = seedEnd ?? 10, BaseRandom = new Random(randomSeed ?? defaultSeed), BaseDateTime = DateTime.Now, CurrentPropertyName = propertyName }; return new SeedCore(gen).SeedList(iDictionary); } // Example of a custom method that only returns one instance of <T> instead of a list public static T SeedOne<T> (this T type, int? randomSeed = null) where T : new() { return new List<T> ().Seed(seedEnd: 1, randomSeed: randomSeed).Single(); } } }

Custom Extension Methods

To Run Code:

Create a folder called Extensions and copy the code above 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.

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.

Title Album Artist Released
1 Take Your Time Partial Mission The Bad Ones 2/10/2017
2 Life Leasons Tin Can Man Joe Jetson 3/30/2018
3 Seredipity2 Fraz the Queen Like More Water 6/22/2019
4 The Man and the Stars Gangland Frame Fitzi Louis 12/17/2016
5 Seredipity2 Fraz the Queen Like More Water 7/4/2019
6 Take Your Time Thanks for the Fish Stalingrad Cowboys 3/27/2017
7 Groovy Tunes One at a Time Audacious Criminal 5/7/2016
8 Life Leasons Freshly Rejected Sea Lion 2/9/2018
9 Country Shack Hip Juice Trixy OldHouse 9/16/2019
10 The Will and the Way Freshly Rejected Sea Lion 1/8/2018

SeedOne Extension Method

An additional method included in the example is SeedOne() which is implementation for just getting back a fully-populated single instance of the type. It is an extension on any class that implements the new() constructor (with no constructor parameters). While perhaps not the most efficient code, it shows how you can create alternative extensions that utilize SeedPacket code generation.

Id:1
Guid:55a346d5-3e27-fa1c-71f1-0f2ee19eb323
Ceo:John Smith
CompanyName:Tyrell
Net Worth:$264.75k
Notes:Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eisumod tempor incidicunt ut. Labore et dolore magna aliqua ut enim, ad minim veniam quis nostrud exercitation ullamco laboris nisi. Ut aliquip ex ea. Commodo consequat duis aute irure dolor in reprehenderit in voluptate velit esse.
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) { } 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(); AdvancedRules(); 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."); } } public void AdvancedRules() { // Example. Obviously this could be more extensive... var advanceRules = new List<Rule<(){ new Rule(typeof(DateTime), "Create%", g => g.BaseDateTime.AddDays (g.RowRandom.Next(-30, 1)), "DateTimeInLastMonth" ), new Rule(typeof(string),"Description%", g => Funcs.GetElementRandom(g, "Description"), "Description", "Gets Description from custom XML file" ), new Rule(typeof(string), "Ceo", g => Funcs.GetElementNext(g, "FirstName") + " " + Funcs.GetElementNext(g, "LastName"), "Random CEO Name"), }; this.Rules.AddRange(advanceRules, true); } } }

Custom Generators

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}.

Another extensibility point is demonstrated in the code above 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.

CEO Company City State
John Smith Tyrell York IN
Patricia Johnson Wayne Corp Austin NV
Michael Williams Sunrise Systems Kingston TX
Susan Jones Spacely Athens ID
William Brown Sunrise Systems Kingston UT
Mary Davis Oscorp Co London IA
Robert Miller Globax Washington AR
Sarah Wilson Jackson Corp Jackson MT
Peter Moore MultiNational Inc Eastdale VA
Ann Taylor Jackson Co Jackson MO
Roger Anderson Mega LLC Gotham NM
Theresa Thomas Virtucon Lincoln CO

© 2018 Will Crowther - SeedPacket