This article will present a way to create a Schema attribute for Entity Framework in .NET Framework.
Some people still use .NET Framework, hopefully the latest supported version .NET Framework 4.8, due to compability reasons and legacy code and having a Schema attribute is not supported in Entity Framework for .NET Framework, still in Entity Framework 6.5.0 which is the newest version.
This is similar to the way we can set Schema via attribute on an entity in Entity Framework .NET Core 8.
The code presented in the article is in the following Github repo :
https://github.com/toreaurstadboss/BulkOperationsEntityFramework
The Schema attribute is a simple marker attribute where the schema name to be set to the entity (class) upon it is applied on.
SchemAttribute.cs
using System;
namespace BulkOperationsEntityFramework.Attributes
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class SchemaAttribute : Attribute
{
private readonly string _schemaName;
public SchemaAttribute(string schemaName)
{
_schemaName = schemaName ?? "dbo"; //fallback to default schema 'dbo' if null is passed in here
}
public string SchemaName => _schemaName;
}
}
The attribute will be used as an attribute-based code convention for Entity Framework.
First off, the code conventions are set up using a helper extension. Note that it is not necessary to cast
this to DbContext here, it just makes it more readable that we are passing in DbContext here.
ApplicationDbContext.cs
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
// more code here if needed
modelBuilder.ApplyCustomCodeConventions((DbContext)this); // Apply custom code conventions based on DbSet types. Pass in db context.
// more code here if needed
}
The helper method
ApplyCustomCodeConventions will loop through all the DbSet generic properties of the passed in DbContext. DbModelBuilder is the instance that the helper extension method provides more functionality on.
ModelBuilderExtensions.cs
using BulkOperationsEntityFramework.Attributes;
using System.Data.Entity;
using System.Linq;
using System.Reflection;
namespace BulkOperationsEntityFramework
{
public static class ModelBuilderExtensions
{
/// <summary>
/// Applies custom code conventions to the specified <see cref="DbModelBuilder"/> instance based on the <see
/// cref="DbSet{TEntity}"/> types defined in the provided <see cref="DbContext"/>.
/// </summary>
/// <remarks>This method inspects the <see cref="DbSet{TEntity}"/> properties of the provided <see
/// cref="DbContext"/> and applies schema conventions to each entity type. It is typically used to enforce
/// custom schema rules or configurations during model creation.</remarks>
/// <param name="modelBuilder">The <see cref="DbModelBuilder"/> instance to which the conventions will be applied.</param>
/// <param name="context">The <see cref="DbContext"/> containing the <see cref="DbSet{TEntity}"/> types to analyze.</param>
public static void ApplyCustomCodeConventions(this DbModelBuilder modelBuilder, DbContext context)
{
var dbSetTypes = context
.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
.Select(p => p.PropertyType.GetGenericArguments()[0]);
foreach (var type in dbSetTypes)
{
ApplySchemaAttributeConvention(modelBuilder, type);
}
}
/// <summary>
/// Adds a convention to apply the Schema attribute to set the schema name of entities in the DbContext.
/// </summary>
/// <param name="modelBuilder"></param>
/// <param name="type"></param>
private static void ApplySchemaAttributeConvention(DbModelBuilder modelBuilder, System.Type type)
{
var schema = type.GetCustomAttribute<SchemaAttribute>(false)?.SchemaName;
if (schema != null)
{
var entityMethod = typeof(DbModelBuilder).GetMethod("Entity").MakeGenericMethod(type);
var entityTypeConfiguration = entityMethod.Invoke(modelBuilder, null);
var toTableMethod = entityTypeConfiguration.GetType().GetMethod("ToTable", new[] { typeof(string), typeof(string) });
toTableMethod.Invoke(entityTypeConfiguration, new object[] { type.Name, schema });
}
}
}
}
The schema attribute could also set one and one entity in the
OnModelConfiguration method like for example the following:
ApplicationDbContext.cs
modelBuilder.Types().Where(p => p.GetCustomAttributes(false).OfType<SchemaAttribute>().Any())
.Configure(t => t.ToTable(t.ClrType.Name, t.ClrType.GetCustomAttribute<SchemaAttribute>().SchemaName ?? "dbo")); //add support for setting Schema via Schema attribute using custom code convention
But the code above is much better but in a reusable helper method as shown higher up in this article so you can easier just paste in the helper method and do a much cleaner call in your DbContext's
OnConfiguring method.
Note that the Schema attribute is available if you use .NET Core Entity Framework Core 8, that is using .NET 8.
Example usage:
ArchivedUser.cs
using BulkOperationsEntityFramework.Attributes;
namespace BulkOperationsEntityFramework.Models
{
[Schema("Archive")]
public class ArchivedUser
{
public int Id { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
}
}
When this is set up, we can easily set up the schema of an entity by just attributing the table using this Schema attribute.
Share this article on LinkedIn.
No comments:
Post a Comment