Most of ORMs has moved to Code first approach where everything is derived/initialized from codes rather than DB side. In this situation, it is better to set data through codes only. We would be looking through simple technique where we would be Seeding data through Codes.
I would be using UnitOfWork and Repository pattern for implementing Data Seeding technique. This can be applied to any data source MongoDB, EF, or any other ORM or DB.
Things we would be doing.
- Creating a base class for easy usage.
- Interface for Seed function for any future enhancements.
- Individual seed classes.
- Configuration to call all seeds.
- AspNet core configuration to Seed data through Seed configuration.
This would be just helpful for individual data seeds.
If you see, these are pretty simple queries statements. There are two major things, checking if any data exists and then inserting based on condition. In a similar pattern, we can have multiple conditions and insert statements with multiple classes based on Domain/Data models.
This has to be configured on Startup.cs
The above code would do DB migration and seeding of data in DB.
I would be using UnitOfWork and Repository pattern for implementing Data Seeding technique. This can be applied to any data source MongoDB, EF, or any other ORM or DB.
Things we would be doing.
- Creating a base class for easy usage.
- Interface for Seed function for any future enhancements.
- Individual seed classes.
- Configuration to call all seeds.
- AspNet core configuration to Seed data through Seed configuration.
Creating a base class for easy usage
public abstract class BaseSeed<TModel>
where TModel : class
{
protected readonly IMyProjectUnitOfWork MyProjectUnitOfWork;
public BaseSeed(IMyProjectUnitOfWork MyProjectUnitOfWork)
{
MyProjectUnitOfWork = MyProjectUnitOfWork;
}
protected void Save(TModel model, IRepository<TModel> repository)
{
repository.Create(model);
MyProjectUnitOfWork.Save();
}
}
This would be just helpful for individual data seeds.
Interface for Seed function for any future enhancements
public interface ISeed
{
void Seed();
}
Individual seed classes
public class LookupSeeder
: BaseSeed<Lookup>, ISeed
{
public LookupSeeder(IMyProjectUnitOfWork MyProjectUnitOfWork)
: base(MyProjectUnitOfWork)
{
}
public void Seed()
{
InsertState();
InsertLanguage();
}
private readonly Lazy<List<Lookup>> States = new Lazy<List<Lookup>>(() => new List<Lookup>
{
new Lookup {
LookupType = LookupType.State,
LookupValue= "ALABAMA",
LookupIdString= "AL"
},
new Lookup {
LookupType = LookupType.State,
LookupValue= "MyProject",
LookupIdString= "AK"
},
new Lookup {
LookupType = LookupType.State,
LookupValue= "MINNESOTA",
LookupIdString= "MN"
},
new Lookup {
LookupType = LookupType.State,
LookupValue= "MISSISSIPPI",
LookupIdString= "MS"
},
new Lookup {
LookupType = LookupType.State,
LookupValue= "NEVADA",
LookupIdString= "NV"
},
new Lookup {
LookupType = LookupType.State,
LookupValue= "NEW HAMPSHIRE",
LookupIdString= "NH"
},
}
);
/// <summary>
/// Initial inserts for <see cref="LookupType.State"/>
/// </summary>
private void InsertState()
{
var lookupRepo = MyProjectUnitOfWork.LookupRepository;
if (!lookupRepo.Any(lookup => lookup.LookupType == LookupType.State))
{
lookupRepo.Create(States.Value);
MyProjectUnitOfWork.Save();
}
}
#endregion " Initial records for State"
/// <summary>
/// Inserts the <see cref="LookupType.Language"/> lookups.
/// </summary>
private void InsertLanguage()
{
var lookupRepo = MyProjectUnitOfWork.LookupRepository;
if (!lookupRepo.Any(lookup => lookup.LookupType == LookupType.Language))
{
lookupRepo.Create(new List<Lookup> {
new Lookup {
LookupType = LookupType.Language,
LookupIdString = "ENGLISH",
LookupValue = "ENGLISH"
},
new Lookup {
LookupType = LookupType.Language,
LookupIdString = "SPANISH",
LookupValue = "SPANISH"
},
});
MyProjectUnitOfWork.Save();
}
}
}
If you see, these are pretty simple queries statements. There are two major things, checking if any data exists and then inserting based on condition. In a similar pattern, we can have multiple conditions and insert statements with multiple classes based on Domain/Data models.
Configuration to call all seeds
This is just a simple class that would call all individual Seeder classes based on domain models. /// <summary>
/// Database seeding configuration.
/// </summary>
public static class DataSeederConfig
{
/// <summary>
/// Seeds the database.
/// </summary>
/// <param name="serviceCollection">The service collection.</param>
public static void SeedDatabase(this IServiceProvider serviceCollection)
{
var uow = serviceCollection.GetService<IMyProjectUnitOfWork>();
Seed(uow);
}
/// <summary>
/// Seeds the specified MyProject unit of work.
/// </summary>
/// <param name="myProjectUnitOfWork">The MyProject unit of work.</param>
private static void Seed(IMyProjectUnitOfWork myProjectUnitOfWork)
{
// Order is important as id generation are dependent on each other.
// Parent to child item is followed.
new LookupSeederSql(myProjectUnitOfWork).Seed();
myProjectUnitOfWork.Save();
}
}
AspNet core configuration to Seed data through Seed configuration
Now, we are all set for the final piece. There is only one endpoint to configure entire Data seeds through DataSeederConfig class having extension method SeedDatabase.This has to be configured on Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
serviceScope.ServiceProvider.GetService<MyProjectContext>()
.Database.Migrate();
serviceScope.ServiceProvider.SeedDatabase();
}
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
}
The above code would do DB migration and seeding of data in DB.
Comments
Post a Comment