When you’re diving into Entity Framework, one of the main tasks that you will be doing is generating migration, and what comes with it, is naming migration files. Well, let me tell you, naming them can be a real head-scratcher. They say, “The hardest thing in programming is naming things,” and it holds true here too.
When I first started, it was a real challenge for me to come up with short, concise, and clear names that described the change in migration, but you know what they say about challenges – they’re just opportunities for solutions! So, I came up with my own little naming system and wanted to share it with you. So we can brainstorm together and make it better.. so let’s dive in.
Naming Migration Files: The conventional way
Starting out on the road of learning EF, we all follow the conventional approach, You know, the one we picked up from documentation and online tutorials. It’s all about making small, focused migrations that correspond to specific changes in our database.
doing a small search on Google with “entity framework naming migration files” you’ll find that suggestions from Stack Overflow, Microsoft Docs, and various blogs consistently advocate for a similar approach: implement small changes in chunks and craft migration names that reflect these changes.
Honestly, I don’t favor this approach. In the real world, we often work on large projects that involve many changes. If we stick to this method, we’ll accumulate numerous migration files, each reflecting small adjustments. Consequently, the Migrations folder will become cluttered, making it cumbersome to navigate through.
to better understand this, let’s make up a situation, let’s say we are working on a Task management app, the app only has one Table/Class called “Task” with the following: ID, Name, and CreatedOn. and we have one migration called “AddTask”.
Now you received a change request, your job is to:
- add 3 new properties/columns to “Task”: Status<Enum>, Description, LastModifiedOn.
- create a new table/class called “Comment” with ID, TaskId, Title, Content, and Date, where each task may have 0 or more comments.
- update property/column CreatedOn, change name to Date.
so using the conventional way, we can see that the last 2 changes are easy to name, and each will be in its own migration, for table creation we can do something like “AddTaskComment”, and for the last one “UpdateTaskCreatedOnToDate”.
what about the first one, are you planning to combine these changes into a single migration, or would you prefer each change in its own migration? If we combine them, the name could be something like “AddStatusDescriptionLastModifiedOnToTask.” However, if we go with separate migrations for each change, it would be “AddPropXToTask” for each property (where PropX is the name of the property).
with this, you will end up with either 3 or 5 migration files.
The new way
my method is very simple, I just don’t name the migration files 😃
don’t get me wrong, I do, but differently, let me explain and you will see how simple it is.
all migration files that I generate have a simple naming convention [prefix]_[counter] / [prefix]_X[counter] here are some examples:
- MG_01 / MG_X01
- OGM_02 / OGM_X02
plus a “_MigrationHistory.md” file to keep track of the changes, more on that later.
now that you have seen how the migration files are named, let’s explain the logic behind it. starting with the counter the easy part, the counter is just an incrementation of the last migration, let’s say that the last migration that you have created is “MG_01” so your next migration will be named, you guessed it, “MG_02”, no more headaches, no more wasting time on how I should name it, etc. increment and relax.
now the prefix part and as you can see there are two, [prefix]_ and [prefix]_X, let’s start with the first one.
if you have one DbContext, it is simple, just keep the prefix as “MG” (MG is taken from Migration) so here the prefix only plays a role in the name of the class, as you know the class name cannot start with a number.
but if you have multiple DbContext targeting the same database (for example you are working on a modular monolithic) then the prefix has a role, here each DbContext or Module I give a unique prefix, for example, “OGM” that I put above, it for “Organizations Management”, another example “Account Management” it will be ACM, etc.
here is a screenshot from the “__efmigrationshistory” table of a project that I am currently working on:
here I have 4 modules each with its own DbContext targeting the same database, so a migration for each one, and you can see that each one has its unique prefix.
if you do not give a unique prefix for each module/DbContext you will end up with something like this
each one of these MG_01 corresponds to a module/DbContext, it is a mess, you not going to know which one is which, I learned it the hard way, and that is why I started prefixing each module with its unique prefix value.
what about the X in [prefix]_X!
usually you not going to be working on the project alone. so when you work on a task you create a git branch and you start doing your changes, whenever you are on your own branch add the X, and the reason behind it is to not have name conflicts, imagine in the main branch the last migration is MG_02 so you create a branch and you did a change now the next migration will be MG_03, but someone else already created MG_03 on the main branch, with that when you merge main with your current branch you will have two migrations with the same name, that why we add the X, but we don’t keep it. but how?
so let’s take the example above, on your branch you have generated 3 migrations and now it is time to merge your changes with the main branch (keep in mind that all the changes on your branch are local changes, they reflect the changes on your local dev database), so what you should do is as follows:
- First, you need to undo all the migrations up to the last migration you brought from the main branch in this case MG_02:
- > update-database MG_02
- > remove-migration (keep doing this command until all X migrations are removed)
- Second, merge the main branch with your local branch to get the latest changes and migrations.
- Third, add-migration MG_03: because the last migration is MG_02 we just increment it. this step will create a new migration with all the changes you’ve made combined into one migration file.
- Fourth, update the “_MigrationHistory.md” file.
- Finally, merge your branch with the main branch.
what we have done here is called migration reset, this approach is also used to reset the entire migrations history, for example, if you have 50 migrations files “MG_50” you can plan a reset to MG_01 on the next version of your app, and only have one migration file, it like you starting from the beginning. (I will try to cover this in more detail in a separate blog post).
So, here we are resetting migrations on a branch level so that when you finish working on your task, you reset all the migrations you have created (with the “X” prefix) to only have one migration file containing all the changes you’ve made while working on that task.
What about “_MigrationHistory.md” file?
I created a file named “_MigrationHistory.md” and I put it with the migrations inside the “Migrations” folder, this file acts like the changelog file, here is where we describe what has happened in each Migration. here is an example of the content of the file:
this file contains the `FeatureDbContext` migrations history.
migration files prefix: OGM
**[OGM_02]**:
- Added FiscalIdentifiers collection as Json to Organization.
**[OGM_01]**:
- Initial database migration.
also, I added 2 lines to the top of the file indicating the name of the DbContext this migration belongs to, and the prefix used for the migration files.
And that’s all there is to it. I have been using this approach for many years, both in personal projects and at work as well, it is easy, simple, and straightforward.
now, let’s go back to the example above, using my approach you will end up with 1 migration file. called “MG_02” (“MG_02” because we will have “MG_01” containing the first migration that represents “AddTask”).
and the “_MigrationHistory.md” file will have the following content:
this file contains the `ApplicationDbContext` migrations history.
migration files prefix: MG
**[MG_02]**:
- added new properties/columns to "Task": Status, Description, LastModifiedOn.
- added a new table/class "Comment".
- updated property/column CreatedOn, change name to Date.
**[MG_01]**:
- Initial database migration.
Conclusion
I’m curious to hear your thoughts and feedback on this approach, Do you find it intuitive and efficient? do you have suggestions for improvement? I’m always open to refining my techniques, so any insights you have would be greatly appreciated.
additionally, I’m contemplating the idea of implementing an automatic way of generating file names, which could further streamline the process.
I hope you’ve found it helpful and enjoyable. Let’s keep the conversation going!