Extending
how to extend SeedPacket functionality
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 | 3/10/2024 |
2 | Extemporaneous | Quartet | Rap Crew | 10/13/2023 |
3 | Only One | Know Your Friends | Hurt No More | 2/22/2024 |
4 | The Man and the Stars | Gorilla Joint | Fiddly Bits | 3/24/2026 |
5 | Troubles Will Come | Fresh | Trixy OldHouse | 7/6/2023 |
6 | Groovy Tunes | Thanks for the Fish | Temper Flare | 4/26/2026 |
7 | You Will Find Love | Egg in the Eye | Jon ColdRidge | 1/12/2026 |
8 | Take Your Time | Fresh | Mac Truck | 9/18/2023 |
9 | A Brighter Shade of Gray | Fresh | Temper Flare | 4/23/2025 |
10 | The Stream | The Asking | Audacious Criminal | 9/5/2026 |
11 | Seredipity2 | Hope for the Masses | The Bad Ones | 9/5/2024 |
12 | A Brighter Shade of Gray | Fresh | Like More Water | 10/14/2024 |
13 | Take Your Time | Freshly Rejected | Boodle Jones | 10/8/2024 |
14 | Don't Worry | Partial Mission | Dylan Dylan | 1/25/2026 |
15 | A Brighter Shade of Gray | Gangland Frame | Boodle Jones | 1/11/2025 |
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 |
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")}";
}
}
}