Entity Framework Core Generator

To use Entity Framework Core Generator, you need to install the .NET Core Global Tool.

dotnet tool install --global EntityFrameworkCore.Generator

After the tool has been install, the efg command line will be available. Run efg --help for command line options

Initialize Configuration

Entity Framework Core Generator has many options available to customize the generated output. The initialize command is used to create the configuration yaml file and optionally set the database connection string. Update the configuration file to configure the generated output.

The following command will create an initial generation.yaml configuration file as well as setting a user secret to store the connection string.

Generate Entity Framework Core Model

In order to use Entity Framework Core, you need a DbContext, entity classes and mapping from a table to those entities. Entity Framework Core Generator creates these for you from the database. To generate the files, run the generate command

efg generate

Generation Output

The generate command will create the follow files and directory structure by default. The root directory defaults to the current working directory. Most of the output names and locations can be customized in the configuration file

Data Context Output

The EntityFramework DbContext file will be created in the Data directory.

Entities Output

The Entities directory will contain the generated source file for entity class representing each table.

Mapping Output

The Mapping directory contains a fluent mapping class to map each entity to its table.

Model Output

Entity Framework Core Generator has an option to create view models for each entity. The output will go in the Domain directory by default.

Generated Model Cleanup

Entity Framework Core Generator supports safe regeneration via region replacement and source code parsing. A typical workflow for a project requires many database changes and updates. Being able to regenerate the entities and associated files is a huge time saver.

Rename Property

The code generator makes its best attempt to convert names to there plural form using the Humanizer library. In some cases it fails. The first cleanup to do is to rename the TrackerContext.TaskExtendeds property to TrackerContext.TaskExtended.

When the generate command is re-run, this refactor will be saved.

Identifier Interface

In order to handle entities in a generic way, we’ll need to add some interfaces to them. We’ll add the IHaveIdentifier to all entities and models.

Notice the file has some regions like #region Generated .... These regions are what get replace the next time efg generate is called. Since the interface was added outside of those regions, it will not get overwritten.

Web API

You’ll need to add a few more things to your Web API project to get things going.

Application Startup

You’ll need to change the application startup to register the Entity Framework context as well as register the AutoMapper profiles.

usingAutoMapper;usingMicrosoft.AspNetCore.Builder;usingMicrosoft.AspNetCore.Hosting;usingMicrosoft.AspNetCore.Mvc;usingMicrosoft.EntityFrameworkCore;usingMicrosoft.Extensions.Configuration;usingMicrosoft.Extensions.DependencyInjection;usingSwashbuckle.AspNetCore.Swagger;usingTracker.Data;namespaceTracker{publicclassStartup{publicStartup(IConfigurationconfiguration){Configuration=configuration;}publicIConfigurationConfiguration{get;}// This method gets called by the runtime. Use this method to add services to the container.publicvoidConfigureServices(IServiceCollectionservices){// sharing the user secret configuration filevarconnectionString=Configuration.GetConnectionString("Tracker");services.AddDbContext<TrackerContext>(options=>options.UseSqlServer(connectionString));// register AutoMapper profilesservices.AddAutoMapper();services.AddMvc().AddFluentValidation(fv=>fv.RegisterValidatorsFromAssemblyContaining<Startup>())// register validators.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);// Register the Swagger generatorservices.AddSwaggerGen(c=>{c.SwaggerDoc("v1",newInfo{Title="Tracker API",Version="v1"});});}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv){if(env.IsDevelopment()){app.UseDeveloperExceptionPage();}else{app.UseHsts();}// Enable middleware to serve generated Swagger as a JSON endpoint.app.UseSwagger();// Enable middleware to serve swagger-ui, specifying the Swagger JSON endpoint.app.UseSwaggerUI(c=>{c.SwaggerEndpoint("/swagger/v1/swagger.json","Tracker API");});app.UseHttpsRedirection();app.UseMvc();}}}

Base Controller

To make the basic read, create and update endpoints easier, create a base controller class like the following.

Documentation

Installation

To install EntityFrameworkCore.Generator tool, run the following command in the console

dotnet tool install --global EntityFrameworkCore.Generator

After the tool has been install, the efg command line will be available. Run efg --help for command line options

Generate Command

Entity Framework Core Generator (efg) creates source code files from a database schema. To generate the files with no configuration, run the following

efg generate -c <ConnectionString>

Replace <ConnectionString> with a valid database connection string.

Generation Output

The generate command will create the follow files and directory structure by default. The root directory defaults to the current working directory. Most of the output names and locations can be customized in the configuration file

Data Context Output

The EntityFramework DbContext file will be created in the root directory.

Entities Output

The entities directory will contain the generated source file for entity class representing each table.

Mapping Output

The mapping directory contains a fluent mapping class to map each entity to its table.

Initialize Command

The initialize command is used to create the configuration yaml file and optionally set the connection string. The configuration file has many options to configure the generated output. See the configuration file documentation for more details.

The following command will create an initial generation.yaml configuration file as well as setting a user secret to store the connection string.

efg initialize -c <ConnectionString>

When a generation.yaml configuration file exists, you can run efg generate in the same directory to generate the source using that configuration file.

Regeneration

Entity Framework Core Generator supports safe regeneration via region replacement and source code parsing. A typical workflow for a project requires many database changes and updates. Being able to regenerate the entities and associated files is a huge time saver.

Region Replacement

All the templates output a region on first generation. On future regeneration, only the regions are replaced. This keeps any other changes you’ve made to the source file.

When the generate command is re-run, the Generated Constructor, Generated Properties and Generated Relationships regions will be replace with the current output of the template. Any other changes outside those regions will be safe.

Source Parsing

In order to capture and preserve Entity, Property and DbContext renames, the generate command parses any existing mapping and DbContext class to capture how things are named. This allows you to use the full extend of Visual Studio’s refactoring tools to rename things as you like. Then, when regenerating, those changes won’t be lost.

Database Schema

The database schema is loaded from the metadata model factory implementation of IDatabaseModelFactory. Entity Framework Core Generator uses the implemented interface from each of the supported providers similar to how ef dbcontext scaffold works.

View Models

Entity Framework Core Generator supports generating Read, Create and Update view models from an entity. Many projects rely on view models to shape data. The model templates can be used to quickly get the basic view models created. The model templates also support regeneration so any database change can easily be sync’d to the view models.

Configuration

Configure the Contact properties and collections.

EntityChange.Configuration.Default.Configure(config=>config.Entity<Contact>(e=>{// set the FirstName display namee.Property(p=>p.FirstName).Display("First Name");// compare the Roles collection by string equalitye.Collection(p=>p.Roles).CollectionComparison(CollectionComparison.ObjectEquality).ElementEquality(StringEquality.OrdinalIgnoreCase);// set how to format the EmailAddress entity as a stringe.Collection(p=>p.EmailAddresses).ElementFormatter(v=>{varaddress=vasEmailAddress;returnaddress?.Address;});}).Entity<EmailAddress>(e=>{e.Property(p=>p.Address).Display("Email Address");}));

Concepts

Queue

A queue is equivalent to a MongoDB collection. The name of the queue will match the MongoDB collection name.

Queue names must be alphanumeric, without spaces or symbols.

It is a good practice to suffix the queue name with Queue.

Message

A message is the high level object that is a generic definition of a messages. The message contains processing level information. The message object is automatically created and updated by the Fluent API and should not be updated directly by the publisher or subscriber.

Data

Data is the message payload to be processed with the message. Use data to pass information you need to process the message.

The data object must be serializable by MongoDB driver.

It is a good practice to have one queue per data object being passed to limit confusion and to maintain simplicity when subscribing to the queue.

Publish

Publishing a message adds the message with the corresponding data to a queue for processing.

Subscribe

In order to process a message on a queue, an application needs to subscribe to a queue. There can be many subscribers to a queue to scale the load across processes. A subscriber can also set the worker count to scale the number of processing threads for that subscriber.

The framework ensures that only one subscriber can process a messages.

Queue Configuration

The queue configuration is used to set default values on messages published to a queue.

Properties

Connection is the app.config connection string name used to connect to MongoDB.

Name is the name of the queue to configure. Retry is the number of times the message should be retried on error. Set to zero, default, to not retry. Priority is the default priority to publish the message with. ResponseQueue is the name of the queue where responses should be sent.

Publish Message

To publish a message to a queue, use the fluent api.

varmessage=awaitMessageQueue.Default.Publish(m=>m.Queue(SleepMessage.QueueName).Data(sleepMessage).Correlation("321B4671-3B4C-4B97-8E81-D6A8CF22D4F0").Description("User friendly description of the message").Priority(MessagePriority.Normal).Retry(1));

Properties

RequiredQueue is the name of the queue to publish to. Data is the object to pass in the message. Used to process the message by the subscriber.

OptionalCorrelation is an identifier used to link messages together. Description is a user friendly description of the message.

OverridesRetry is the number of times the message should be retried on error. Priority is the default priority to publish the message with. ResponseQueue is the name of the queue where responses should be sent.

Notes

When setting the Data property, the message Name will be set to the Type name of the data object.

When setting the Data property and Description hasn’t been set, the data object ToString() value will be set as the description.

If the underlying storage collection doesn’t exist, it will be created on first publish

Subscribe to Message

To subscribe to a queue, use the fluent api. The subscribe handler must implement IMessageSubscriber.

Properties

RequiredQueue is the name of the queue to publish to. Handler is the class that implements IMessageSubscriber. This is what processes the message.

OptionalWorkers is the number of worker processes. ExpireError is how long to keep error messages. ExpireWarning is how long to keep warning messages. ExpireSuccessful is how long to keep successful messages. PollTime is the amount of time between work polling. If using Triggger, set to longer time.
Retry is a class that implements IMessageRetry. IMessageRetry controls if an error message should be retried. Timeout is the amount of time before a processing message times out. TimeoutAction is how to handle timed out messages. Options are Fail or Retry. Trigger to enable monitoring of the oplog for changes to trigger processing.

Message Service

In order for the message subscribers to process messages off queue, the MessageService needs to be created and Start called. Note, the MessageService.Stop() method tries to gracefully stop by waiting for active processes to finish.

_messageService=newMessageService();// on service or application start_messageService.Start();// on service stop. _messageService.Stop();

Process Locks

The library has supports distributed locks. The following are the lock types supported.

DistributedLock Distributed Lock manager provides synchronized access to a resources over a network ThrottleLock Throttle Lock Manager controls how frequent a process can run

This is an example of using the DistributedLock.

varlockName="PrintMessage";// get MongoDB collection to store lockvarcollection=GetCollection();// create lock with timeout, max time it will wait for lock, of 5 minutesvarlocker=newDistributedLock(collection,TimeSpan.FromMinutes(5));// acquire lock; if can't, it will retry to get lock up to timeout valuevarresult=locker.Acquire(lockName);if(!result)return;// acquire lock timeouttry{// do processing here}finally{// release locklocker.Release(lockName);}

Generator.Default.Configure(c=>c.Entity<OrderLine>(e=>{// random number between 1 and 10e.Property(p=>p.Quantity).IntegerSource(1,10);// between 100 and 1,000e.Property(p=>p.UnitAmount).DecimalSource(100,1000);}));

Profiles

DataGenerator support class profiles to make configuration easier. To create a profile, inherit from the MappingProfile<T> base class.

Smart Data Sources

CitySource - Random city name from a list of the largest US cities CompanySource - Random company name from a list of fortune 500 companies CreditCardSource - Random credit care number EmailSource - Random email address using common domains EnumSource - Random value from available enum values FirstNameSource - Random first name from 100 common first names IdentifierSource - Random identifier value LastNameSource - Random last name from 100 common last names LoremIpsumSource - Random lorem ipsum text MoneySource - Random dollar amount between 0 and 10,000 NameSource - Random code name from various sources PasswordSource - Random pronounceable password PhoneSource - Random phone number in US format PostalCodeSource - Random US zip code SocialSecuritySource - Random US Social Security Number StateSource - Random US State StreetSource - Random US house number and street WebsiteSource - Random website from top 100 list