SeedPacket 5.0
the LINQ data generator for .NET 5.0 & .NET 6.0
created by Will Crowther
Overview
SeedPacket is for quickly seeding data in .net for graphic mockups, Rapid Application Development (RAD), unit testing, prototyping, data generation, database seeding, and unit testing. Written in C#, it is easy to use, with a customizable, and powerful rules engine that can pull data from an external source such as an Xml/Json file or string.
How It Works
Similar to a LINQ statement, SeedPacket adds a .seed() extension method onto IEnumerable that fills the list with fully populated elements. The rules engine keys off the datatype or interface, and name of an item's properties so that the instance is filled with data that is appropriate to that type. That is to say, "out-of-the-box", email properties will be filled with valid emails, phone numbers filled with phone numbers, and names are names etc.
By default, the rules engine loads up about 30 rules for common situations and will degrade to more generic rules if necessary. If you need to modify the default generated data, the rules are simple to create and modify, and come with a many examples, including using a data generator that pulls from an external source. The randomly generated data can be set to always be static across requests or to be random for each time.
Simple Examples
Creating seed data is as simple as importing SeedPacket from Nuget, adding the SeedPacket.Extensions namespace, and calling .Seed() on an existing an IEnumerable such as List, etc. The table on the right was generated the code in the second example below.
// Example 'User' Class
public class User
{
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public DateTime Created { get; set; }
}
// Creates 10 rows (default)
var users = new List<User>().Seed();
// Create 20 rows (shown to right)
var users = new List<User>().Seed(20);
// Create rows starting with 100 to 200
var users = new List<User>().Seed(100, 200);
You can change the number of rows generated by changing the "rows" number to the right and the page will use javascript to fetch a new list of users generated by SeedPacket on the server. Note that even though the seed records are "randomly" generated, the list begins with the same results on subsequent iterations. This is because we are passing in the same random "seed" integer by default. Now change the "seed" number to the right and you will see that the generated data is always the same for a particular number.
You will see how this can be easily customized when we update what data is generated with some custom rules in the Rules engine. The base data is taken from an embedded datasource, which can be easily overridden with an external XML / Json string or file.
Notice how the seed data generated are well-formed usernames and email addresses. The default rules finds that the property type is a "string" and property contains "email" and "username" and generates the appropriate data, including generating the username from the firstname and lastname. Other data types such as the DateTime "Created" or an Int Id are also be created. The rules match first on data-type, then on match on property name with a last-added prioritization.
What happens when you have a field name and/or datatype is not that common? Generally, the basic rules will catch common patterns and use a simple pattern such as the default of Property Name + the RowNumber for string. If there is not even a basic rule for a particular match, generally the type default is returned.
Of course, the real flexibility comes from being able to easily add your own custom rules. We will get to that in the next section, but lets first look at a more advanced example.
Install SeedPacket using the Nuget Package Manager in Visual Studio.
From the Package Manager Console type:
PM> Install-Package SeedPacket
For more details: https://www.nuget.org/packages/SeedPacket
The SeedPacket source code is available on GitHub.
UserId | FirstName | LastName | Created | |
---|---|---|---|---|
1 | Tiffany | Campbell | tcampbell@virtucon.com | 10/07/2020 |
2 | Robert | King | rking@jacksonco.com | 03/11/2018 |
3 | Nicholas | Scott | nscott@tyrellco.com | 06/17/2020 |
4 | Jacqueline | Miller | jmiller@wonkaco.com | 05/31/2018 |
5 | Mary | Evans | mevans@spacelyco.com | 12/19/2021 |
6 | Treymayne | Rodriguez | trodriguez@compudata.com | 03/25/2021 |
7 | Omar | Harris | oharris@primoco.com | 09/14/2019 |
8 | Courtney | Lee | clee@united.com | 12/25/2018 |
9 | Jesse | Wright | jwright@more.com | 08/19/2018 |
10 | Naomi | Wilson | nwilson@holdings.com | 11/03/2021 |
11 | Zoey | Rodriguez | zrodriguez@morecorp.com | 06/08/2018 |
12 | Susan | Robinson | srobinson@compudataind.com | 12/10/2019 |
13 | Denise | Scott | dscott@wayneco.com | 07/03/2018 |
14 | Janice | Phillips | jphillips@wonkainc.com | 09/16/2019 |
15 | Patricia | Carter | pcarter@virtucon.com | 09/22/2020 |
16 | Crystal | Lopez | clopez@megainc.com | 12/01/2020 |
17 | John | Hall | jhall@umbrella.com | 01/26/2018 |
18 | Jennifer | Hall | jhall@united.com | 03/29/2020 |
19 | Theresa | Parker | tparker@umbrella.com | 05/03/2021 |
20 | Gary | Baker | gbaker@unitedco.com | 12/19/2021 |
You will find that SeedPacket is the most useful when you are in the initial stages of a project and want to rapidly change the structure of data classes without actually having to worry about the data contained in those classes.
A typical development workflow may be:
1. Stub out lists of data using the .Seed method to fill in the structure for your project
2. Create custom Rules to fill in and generate realistic data for mockups and presentations
3. Use SeedPacket for test doubles in your unit tests if desired
4. Migrate to an ORM (Object-Relational Mapping) framework, like Entity Framework, and seed the database using the rules you have created
Designers and UI/UX Developers can focus on designing without worrying about how changes to underlying models and data are affecting their prototypes!
var generator = new MultiGenerator("~/SourceFiles/xmlSeedSourcePlus.xml")
{
Rules =
{
new Rule(typeof(string), "ItemName", g => Funcs.GetNextElement(g, "ProductName"), "ItemName"),
new Rule(typeof(int), "SelectedProduct", g => g.RowRandom.Next(0, 11), "Random product id"),
new Rule(typeof(string), "Ceo", g => Funcs.GetNextElement(g, "FirstName") + " " + func.NextElement(g, "LastName"), "Random CEO Name"),
new Rule(typeof(string),"Description%", g => Funcs.GetRandomElement(g, "Description"), "Description", "Gets Description from custom XML file" ),
new Rule(typeof(List<Item>), "", g => Funcs.GetCacheItemsNext<Item>(g, g.Cache.Items, 10, 10, false), "ItemList"),
},
BaseDateTime = DateTime.Today,
BaseRandom = new Random(1234567)
};
generator.Cache.Items = new List<Item>().Seed(1, 10, generator);
var examples = new List<Example>().Seed(100, 115, generator);
Advanced Example
For more advanced situations, you can pass in a generator class that contains a customizable rules engine. By default, the "Advanced" ruleset is used which contains about thirty common rules and the data is loaded from an internal resource. In the example below a "MultiGenerator" is injected into the Seed method. We are adding five new rules to modify how the data is created.
The first rule applies to the "ItemName" field uses a built-in seedpacket function (Func) that gets the next element from the datasource list "ProductName". It actually overrides a rule that gets random (fake) products and replaces it with one that gets the next item in the sequence. This insures the values will all be unique, if the number of items requested is less than in the source.
The second rule, randomly picks a selected value for the dropdown from 0 to 10 (the C# Random() method picks values less than the max value).
The third rule, randomly picks a first and last name for any string fields matching "CEO"
The fourth rule, matches any string starting with "Description" and gets a random value from the datasoure called "Description". This is not in the default lists that are "built-in" in SeedPacket. In the constructor for the MultiGenerator, you will see that we are now passing in a custom xml file that has all the source data for our lists. Now we have complete control of our source data and Funcs.RandomElement() can pull from any named element.
The fifth custom rule is the most interesting and makes sure that any List<Item> fields are filled from a generated list that has been saved in the generator's Cache. We can populate this generated list from another .Seed call and this allows us to have complex, hierarchical data when needed. Note how we can initially declare the rules, but then need to make sure we fill the cached data before the rules are actually run.
Lastly, notice how we are setting the BaseDateTime value. This can be used as the starting point for all DateTimes that we generate. We are also setting the BaseRandom value. This is the base of the "Random Tree" that all the other randoms such as the RowRandom property use as theire starting point. If we pass in a Random without a seed value, every call will generate different values. If we pass in a Random with a seed value, the data generated will always be the same. We can always pass in a different number seed number if we do not like the data that we get...
Id | Company | City | State | CEO | NetWorth | Products | Description | CompanyGuid |
---|---|---|---|---|---|---|---|---|
100 | Next Co | Oakleaf | GA | Collins | $139.55M | Square, man | 54f931a0-3457-8ab6-6224-8aa04175913e | |
101 | Primo Co | Pinetown | VT | John Smith | $550.67M | Smelly but good | e9ce673c-02ad-456e-0107-df6eff29f370 | |
102 | Tyrell | Washington | MT | Patricia Johnson | $671.91M | Awful | 9df61b4b-d5d8-38ef-3543-60e298309968 | |
103 | Oscorp | Greenville | NY | Michael Williams | $804.51M | Rich! | 90bd1269-0b95-e78f-c1f8-5fda7a1efebd | |
104 | Acme Co | Kingston | AL | Susan Jones | $346.52M | Who knows | d2578136-63d9-99b5-cad8-25cab18dd77a | |
105 | More Co | Oakleaf | AR | William Brown | $150.93M | Bumpy | 0ecd1b54-ca2f-ef87-b9da-c592c03e5848 | |
106 | City Inc | WestLake | PA | Mary Davis | $48.37M | Angular | b23c0acb-9db5-f62a-3a95-245ae5ec642e | |
107 | Stark | Westmoor | AL | Robert Miller | $138.96M | Complicated | 1ae50ea5-1291-23a3-7eea-892d3494b888 | |
108 | Umbrella Corp | Franklin | ND | Sarah Wilson | $703.47M | Angular | 1aec04dd-1979-de90-7fd2-263933509939 | |
109 | More | Brighton | CO | Peter Moore | $902.70M | Transparent | 98a55d7e-ff0d-4cf0-388a-b7dc62066644 | |
110 | Wonka Industries | Eastdale | OH | Ann Taylor | $65.64M | Sunny and bright | 4bce7ccb-9b36-82dc-e0fa-5009bb77d5f6 | |
111 | Umbrella | Franklin | ID | Roger Anderson | $397.28M | Squishy | 7b7a2b2d-2196-e06b-27f1-2c2ac9db12a3 | |
112 | Stark Inc | York | HI | Theresa Thomas | $274.48M | Light purple | 2b379c60-906a-7467-7cdf-5e7484dc4cfa | |
113 | CyberGlobe Corp | Peachtree | VT | Ethan Jackson | $684.89M | Yellow, definitely yellow | ab9205c9-665c-4f83-04a0-d25e4cd6f203 | |
114 | Nakatomi Co | Oakleaf | NY | Crystal White | $167.43M | Hairy and green | ed8182f7-8c8a-ab69-0deb-848efc5aaef7 | |
115 | Stark Co | Gotham | AZ | Phillip Harris | $350.10M | Midnight blue | 7186dd76-935d-dcb1-65cc-1465cebade03 |