Friday Links 0.0.4 - Reverse POCO Generator

This is based on an email I send my .NET team at work

Happy Friday,

Entity Framework Reverse POCO Generator

I’d heard of this tool a few times, but never really checked it out until Rick suggested I send it out this week.

The idea is that you install an extension into Visual Studio, and it adds support for a custom T4 template that can connect to a database, read its schema, and generate all the EntityFramework plumbing you might need to interact with it.

I installed the extension and pointed a connection string at hobby database I have on my local SQL Server Express. Then I added a New Item, finding the reverse POCO item from online. As soon as I saved the new Database1.tt file, it generated Database1.cs with a bunch of neat things in it:

An interface

1
2
3
4
5
6
7
8
9
10
public interface IMyDbContext : System.IDisposable
{
System.Data.Entity.DbSet<Level> Levels { get; set; } // Levels
System.Data.Entity.DbSet<LevelWin> LevelWins { get; set; } // LevelWins
System.Data.Entity.DbSet<User> Users { get; set; } // Users

int SaveChanges();
System.Threading.Tasks.Task<int> SaveChangesAsync();
System.Threading.Tasks.Task<int> SaveChangesAsync(System.Threading.CancellationToken cancellationToken);
}

The name of the interface came from my connection string, which was MyDbContext.

It actually generated two implementations: one was the expected default implementation using EF’s DbContext, but I was also pleasantly surprised to find a FakeDbContext that implemented the interface and could be used in unit tests.

Entity Classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[System.CodeDom.Compiler.GeneratedCode("EF.Reverse.POCO.Generator", "2.21.1.0")]
public class User
{
public int UserId { get; set; } // UserId (Primary key)
public string Username { get; set; } // Username (length: 25)
public bool IsAdmin { get; set; } // IsAdmin
public string HashedPassword { get; set; } // HashedPassword (length: 255)
public string RememberToken { get; set; } // RememberToken (length: 255)
public System.DateTime CreatedAt { get; set; } // CreatedAt
public System.DateTime UpdatedAt { get; set; } // UpdatedAt

public User()
{
IsAdmin = false;
CreatedAt = System.DateTime.UtcNow;
UpdatedAt = System.DateTime.UtcNow;
}
}

Entity Configuration Classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 // Users
[System.CodeDom.Compiler.GeneratedCode("EF.Reverse.POCO.Generator", "2.21.1.0")]
public class UserConfiguration : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<User>
{
public UserConfiguration()
: this("efh")
{
}

public UserConfiguration(string schema)
{
ToTable(schema + ".Users");
HasKey(x => x.UserId);

Property(x => x.UserId).HasColumnName(@"UserId").IsRequired().HasColumnType("int").HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
Property(x => x.Username).HasColumnName(@"Username").IsRequired().HasColumnType("nvarchar").HasMaxLength(25);
Property(x => x.IsAdmin).HasColumnName(@"IsAdmin").IsRequired().HasColumnType("bit");
Property(x => x.HashedPassword).HasColumnName(@"HashedPassword").IsOptional().HasColumnType("nvarchar").HasMaxLength(255);
Property(x => x.RememberToken).HasColumnName(@"RememberToken").IsOptional().HasColumnType("nvarchar").HasMaxLength(255);
Property(x => x.CreatedAt).HasColumnName(@"CreatedAt").IsRequired().HasColumnType("datetime2");
Property(x => x.UpdatedAt).HasColumnName(@"UpdatedAt").IsRequired().HasColumnType("datetime2");
}
}

Since my DB was using a custom schema, I had to tweak one line in the Database1.tt file to not prepend schema names to the entity class names. It prepends, by default, any schema name expect dbo. But finding and changing the option was straightforward and well documented.

This could be a great tool for if we get a project where we need to interact with a legacy database. Rather than hand-rolling all the entity classes, the Lead Dev could install this tool temporarily, run it, check in the output, and uninstall it. If the DB schema was pretty set in stone, and once you got the configuration the way you like, there would be no need to keep regenerating the code. We could save an awful lot of initial development and setup time with this.