Showing posts with label Entity Framework. Show all posts
Showing posts with label Entity Framework. Show all posts

Thursday, March 30, 2017

.Net Core Entity Framework Seperate Entity and Mapping Class

Follow the below steps to have separate entity and mapping definition for entity framework.

This blog assumes you have prior knowledge of the .Net Entity Framework and you have ApplicationDBContext created already.

First we will have BaseEntity Class


namespace TogetherWeCan.Data.IdentityManagement.Model
{
    public class EntityBase
    {
    }
}  
  

Then, we will have EntityMappingBase class with virtual function accpeting ModelBuilder as a parameter.


using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.Text;

namespace TogetherWeCan.Data.IdentityManagement.Model
{
    public abstract class EntityMappingBase<T> where T : EntityBase
    {

        public virtual void BuildAction(ModelBuilder builder)
        {
            builder.Entity<EntityBase>();
        }

    }
}

We are going to define two Entity Class.

  1. Group
  2. GroupTwo


namespace TogetherWeCan.Data.IdentityManagement.Model
{
    public class Group : EntityBase
    {
        public Guid GroupId { get; set; }
        public string Name { get; set; }
        public DateTime Created { get; set; }
    }
}
namespace TogetherWeCan.Data.IdentityManagement.Model
{
    public class GroupTwo : EntityBase
    {
        public Guid GroupTwoId { get; set; }
        public string Name { get; set; }
        public DateTime Created { get; set; }
    }
}  
  

Next we will define two mapping for Group and GroupTwo entity class.

  1. GroupMapping
  2. GroupTwoMapping


namespace TogetherWeCan.Data.IdentityManagement
{
    public class GroupMapping : EntityMappingBase<EntityBase>
    {
        public override void BuildAction(ModelBuilder builder)
        {
            var entityBuilder = builder.Entity<Group>();
            entityBuilder.HasKey(p => p.GroupId);
            entityBuilder.Property(p => p.Name).IsRequired();

        }

    }
}
  
namespace TogetherWeCan.Data.IdentityManagement
{
    public class GroupTwoMapping : EntityMappingBase<EntityBase>
    {
        public override void BuildAction(ModelBuilder builder)
        {
            var entityBuilder = builder.Entity<GroupTwo>();
            entityBuilder.HasKey(p => p.GroupTwoId);
            entityBuilder.Property(p => p.Name).IsRequired();

        }

    }
}  


Now we will have ApplicationDBContext class to initialize above mapping
In order to get the runtime assembly and class i have used below packages

For RuntimeEnvironment.GetRuntimeIdentifier() I have used  Microsoft.DotNet.InternalAbstractions;

For  DependencyContext.Default.GetRuntimeAssemblyNames(runtimeId).Where(w => w.FullName.Contains("TogetherWeCan.Data")); I have used Microsoft.Extensions.DependencyModel;



using System;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using TogetherWeCan.Data.IdentityManagement;
using Microsoft.EntityFrameworkCore;
using System.Reflection;
using System.Linq;
using TogetherWeCan.Data.IdentityManagement.Model;
using Microsoft.DotNet.InternalAbstractions;
using Microsoft.Extensions.DependencyModel;

namespace TogetherWeCan.Data
{
    public class ApplicationIdentityDBContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationIdentityDBContext(DbContextOptions<ApplicationIdentityDBContext> options) : base(options)
        {
            
        }
        public DbSet<Group> Group { get; set; }
        public DbSet<GroupTwo> GroupTwo { get; set; }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            var runtimeId = RuntimeEnvironment.GetRuntimeIdentifier();
            var assemblies = DependencyContext.Default.GetRuntimeAssemblyNames(runtimeId).Where(w => w.FullName.Contains("TogetherWeCan.Data"));

            foreach (var assembly in assemblies)

            {
                Assembly newtonsoftJson = Assembly.Load(assembly);
                foreach (var t in newtonsoftJson.GetTypes())
                {
                    var btyp = t.GetTypeInfo().BaseType;
                    if (btyp != null && btyp.IsConstructedGenericType && btyp.GetGenericTypeDefinition() == typeof(EntityMappingBase<>))
                    {
                        var entityBaseMapping = Activator.CreateInstance(t) as EntityMappingBase<EntityBase>;
                        entityBaseMapping.BuildAction(builder);
                    }

                }

            }
   

            base.OnModelCreating(builder);
        }

    }
}
  
  


Testing

For testing in Startup.cs inject ApplicationDBContext in Configure function and use context.Database.Migrate(); to force entity framework run the migration. Don't forget to add migrations.



using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using TogetherWeCan.Data;
using TogetherWeCan.Data.IdentityManagement;

namespace TogetherWeCan
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddDbContext<ApplicationIdentityDBContext>(options => {
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
                });


            services.AddIdentity<ApplicationUser, IdentityRole>()
       .AddEntityFrameworkStores<ApplicationIdentityDBContext>()
       .AddDefaultTokenProviders();

            // Add framework services.
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, ApplicationIdentityDBContext context)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
            context.Database.Migrate();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}
  
  

Saturday, December 3, 2016

Entity Framework Code First Custom Database Initialization and Check if Table Already Exists

In one of our Project we had to use existing database and we wanted to use the code first entity framework as well.

There was requirement to make sure that some of the tables are already existed and all the new ones will be created using the code first approach.

Therefore, I decided to customized the MigrateDatabaseToLatestVersion.

First we will verify that the prerequisite tables are already there and then based on the auto update app setting we will migrate database to latest version.

GiftVoucherDatabaseInitialization 


public class GiftVoucherDatabaseInitialization : MigrateDatabaseToLatestVersion<GiftVoucherEntityTypesConfigurationDbContext, Migrations.Configuration>
    {
        private string IsCodeFirstAutoMigrationEnabled = "IsCodeFirstAutoMigrationEnabled";

        public GiftVoucherDatabaseInitialization(string connectionStringName) : base(connectionStringName)
        {
        }

        public override void InitializeDatabase(GiftVoucherEntityTypesConfigurationDbContext context)
        {
            var queryString = @"
                         SELECT 1 FROM sys.tables AS T
                         INNER JOIN sys.schemas AS S ON T.schema_id = S.schema_id
                         WHERE S.Name = 'dbo' AND T.Name = '{0}'";
            if (context.Database.Exists())
            {
                bool exists = context.Database
                      .SqlQuery<int?>(string.Format(queryString, "tbl_PurchaseVoucher"))
                      .SingleOrDefault() != null;

                if(!exists)
                    throw new ContentNotFoundException("Business Foundataion Entity{PurchaseVoucher} Not Found");

                exists = context.Database
                      .SqlQuery<int?>(string.Format(queryString, "tbl_PurchaseVoucherRedemption"))
                      .SingleOrDefault() != null;

                if (!exists)
                    throw new ContentNotFoundException("Business Foundation Entity{PurchaseVoucherRedemption} Not Found");
            }

            if (IsMigrationEnabled())
            {
                base.InitializeDatabase(context);
            }
        }

        private bool IsMigrationEnabled()
        {
            var migration = ConfigurationManager.AppSettings[IsCodeFirstAutoMigrationEnabled];
            return migration != null && migration.ToLower().Equals("true");
        }
    }

In Global.asax file, we will set the database initializer for above context

Setting Up Database Initializer


        protected void Application_Start()
        {
            // Add the database migration strategy to update the database on app start
            Database.SetInitializer(new GiftVoucherDatabaseInitialization(ConnectionStringConfig.CommerceConnectionStringName));

            // Run the initializer, but don't force
            new GiftVoucherEntityTypesConfigurationDbContext().Database.Initialize(true);
        }


GiftVoucherEntityTypesConfigurationDbContext

The GiftVoucherEntityTypesConfigurationDbContext looks like below



public class GiftVoucherEntityTypesConfigurationDbContext : DbContext
    {
        private readonly ICustomerContextFacade _customerContextFacade;

        public GiftVoucherEntityTypesConfigurationDbContext()
      : this("EcfSqlConnection", null)
     {
      
     }

        public GiftVoucherEntityTypesConfigurationDbContext(string nameOrConnectionString)
            : base(nameOrConnectionString)
        {
            
        }

        public DbSet<PurchaseVoucher> PurchaseVouchers { get; set; }
  public DbSet<PurchaseVoucherRedemption> PurchaseVoucherRedemptions { get; set; }

  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
   // Dynamically load all configuration
   var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
                .Where(type => !string.IsNullOrEmpty(type.Namespace))
                .Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
            foreach (var type in typesToRegister)
            {
                dynamic configurationInstance = Activator.CreateInstance(type);
                modelBuilder.Configurations.Add(configurationInstance);
            }
            
            base.OnModelCreating(modelBuilder);
        }
     }