代码改变世界

Entity Framework 6 中如何获取 EntityTypeConfiguration 的 Edm 信息?(四)

  音乐让我说  阅读(410)  评论(0编辑  收藏  举报

经过上一篇,里面有测试代码,循环60万次,耗时14秒。本次我们增加缓存来优化它。

DbContextExtensions.cs

复制代码
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Mapping;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using EntityFramework.Extensions;
using System.Linq.Expressions;

namespace EntityFrameworkSample
{
    public enum AcquiredEdmType
    {
        TableName,

        FirstPrimaryKeyNameString,

        ColumnName
    }

    public struct CachedEdmInfo
    {
        public EntitySetMapping Mapping { get; set; }

        public EntitySet TableEntitySet { get; set; }

        public Dictionary<string, string> PropertyColumnNameDic { get; set; }
    }

    public static class DbContextExtensions
    {
        private readonly static Dictionary<string, CachedEdmInfo> _mappingCache = new Dictionary<string, CachedEdmInfo>();

        #region 基础方法

        public static string GetTableName<TEntity>(this DbContext context)
        {
            return GetTableName(context, typeof(TEntity));
        }

        public static string GetTableName(this DbContext context, Type type)
        {
            return GetTableNameOrColumnName(context, type, AcquiredEdmType.TableName);
        }

        public static string GetFirstPrimaryKeyName<TEntity>(this DbContext context)
        {
            return GetFirstPrimaryKeyName(context, typeof(TEntity));
        }

        public static string GetFirstPrimaryKeyName(this DbContext context, Type type)
        {
            return GetTableNameOrColumnName(context, type, AcquiredEdmType.FirstPrimaryKeyNameString);
        }

        public static string GetColumnName<TEntity, TProperty>(this DbContext context, Expression<Func<TEntity, TProperty>> propertyExpression)
        {
            return GetTableNameOrColumnName(context, typeof(TEntity), AcquiredEdmType.ColumnName, LambdaHelper.GetPropName(propertyExpression));
        }

        public static string GetColumnName(this DbContext context, Type type, string propertyName)
        {
            return GetTableNameOrColumnName(context, type, AcquiredEdmType.ColumnName, propertyName);
        }

        private static string GetTableNameOrColumnName(this DbContext context, Type type, AcquiredEdmType edmType, string propertyName = null)
        {
            if (context == null)
            {
                throw new ArgumentNullException("dbContext");
            }
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            CachedEdmInfo edmInfo;
            if (_mappingCache.ContainsKey(type.FullName))
            {
                edmInfo = _mappingCache[type.FullName];
            }
            else
            {
                MetadataWorkspace metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;

                // Get the part of the model that contains info about the actual CLR types
                ObjectItemCollection objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));

                // Get the entity type from the model that maps to the CLR type
                EntityType entityType = metadata
                        .GetItems<EntityType>(DataSpace.OSpace)
                              .Single(e => objectItemCollection.GetClrType(e) == type);

                // Get the entity set that uses this entity type
                EntitySet entitySet = metadata
                    .GetItems<EntityContainer>(DataSpace.CSpace)
                          .Single()
                          .EntitySets
                          .Single(s => s.ElementType.Name == entityType.Name);

                // Find the mapping between conceptual and storage model for this entity set
                EntitySetMapping mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
                              .Single()
                              .EntitySetMappings
                              .Single(s => s.EntitySet == entitySet);

                // Find the storage entity set (table) that the entity is mapped
                EntitySet tableEntitySet = mapping
                    .EntityTypeMappings.Single()
                    .Fragments.Single()
                    .StoreEntitySet;

                edmInfo = new CachedEdmInfo
                {
                    Mapping = mapping,
                    TableEntitySet = tableEntitySet
                };
                _mappingCache.Add(type.FullName, edmInfo);
            }
            

            // Return the table name from the storage entity set
            object objTableName = edmInfo.TableEntitySet.MetadataProperties["Table"].Value;
            string tableName = objTableName == null ? edmInfo.TableEntitySet.Name : Convert.ToString(objTableName);

            switch (edmType)
            {
                case AcquiredEdmType.TableName:
                    return tableName;
                case AcquiredEdmType.FirstPrimaryKeyNameString:
                    {
                        var firstKeyProp = edmInfo.TableEntitySet.ElementType.KeyProperties[0];
                        //return tableName + "." + firstKeyProp.Name;
                        return firstKeyProp.Name;
                    }
                case AcquiredEdmType.ColumnName:
                    {
                        // Find the storage property (column) that the property is mapped
                        edmInfo.PropertyColumnNameDic = edmInfo.PropertyColumnNameDic ?? new Dictionary<string, string>();
                        if (edmInfo.PropertyColumnNameDic.ContainsKey(propertyName))
                        {
                            return edmInfo.PropertyColumnNameDic[propertyName];
                        }
                        else
                        {
                            string columnName = edmInfo.Mapping
                            .EntityTypeMappings.Single()
                            .Fragments.Single()
                            .PropertyMappings
                            .OfType<ScalarPropertyMapping>()
                                  .Single(m => m.Property.Name == propertyName)
                            .Column
                            .Name;
                            //写入缓存
                            edmInfo.PropertyColumnNameDic.Add(propertyName, columnName);
                            //实际上,下面的 if 判断肯定为 true,但为了避免后期改动了本方法最上面的代码,所以加一个判断。
                            if (_mappingCache.ContainsKey(type.FullName))
                            {
                                _mappingCache[type.FullName] = edmInfo;
                            }
                            else
                            {
                                _mappingCache.Add(type.FullName, edmInfo);
                            }
                            //return tableName + "." + columnName;
                            return columnName;
                        }
                    }
                default:
                    throw new ArgumentNullException("Invalid argument");
            }
        }

        #endregion

        #region 额外方法

        

        #endregion
    }
}
复制代码

 

Program.cs

复制代码
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Mapping;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using EntityFramework.Extensions;
using System.Linq.Expressions;
using EntityFrameworkSample;

namespace EntityFrameworkSample
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new BloggingContext(@"Data Source=.\SQLExpress;Initial Catalog=TestDB;Persist Security Info=True;User ID=sa;Password=123456"))
            {
                string blogTableName = context.GetTableName<Blog>();
                string postTableName = context.GetTableName<Post>();

                Console.WriteLine("Blog maps to: {0}", blogTableName);
                Console.WriteLine("Post maps to: {0}", postTableName);

                string blogPrimaryKeyName = context.GetFirstPrimaryKeyName<Blog>();
                string postPrimaryKeyName = context.GetFirstPrimaryKeyName<Post>();

                Console.WriteLine("Blog primary key name: {0}", blogPrimaryKeyName);
                Console.WriteLine("Post primary key name: {0}", postPrimaryKeyName);

                System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
                for (int i = 0; i < 600000; i++)
                {
                    context.GetColumnName<Blog, string>(c => c.BlogUrl); //经过测试,循环 60 万次,耗时 3.21 秒
                }
                watch.Stop();
                Console.WriteLine("经过测试,循环 60 万次,耗时 {0} 秒", (watch.ElapsedMilliseconds / (double)1000).ToString("f2"));

                string blogUrlColumnName = context.GetColumnName<Blog, string>(c => c.BlogUrl);
                string postTitleColumnName = context.GetColumnName<Post, string>(c => c.PostTitle);

                //Console.WriteLine("Blog.BlogUrl maps to: {0}.{1}", blogTableName, blogUrlColumnName);
                Console.WriteLine("Post.PostTitle maps to: {0}.{1}", postTableName, postTitleColumnName);

                
            }
            Console.ReadLine();
        }

        
    }

    public class BloggingContext : DbContext
    {
        public BloggingContext(string nameOrConnectionString)
            : base(nameOrConnectionString)
        {
            
        }

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new BlogMap());
            modelBuilder.Configurations.Add(new PostMap());
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
            base.OnModelCreating(modelBuilder);
        }
    }

    public class Blog
    {
        public int Id { get; set; }
        public string BlogUrl { get; set; }

        public List<Post> Posts { get; set; }
    }

    public class Post
    {
        public int Id { get; set; }
        public string PostTitle { get; set; }
        public string Body { get; set; }

        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }

    public class BlogMap : EntityTypeConfiguration<Blog>
    {
        public BlogMap()
        {
            this.HasKey(c => c.Id);
            this.ToTable("t_blog");
            this.Property(c => c.Id).HasColumnName("BlogId");
            this.Property(c => c.BlogUrl).HasColumnName("Url");
        }
    }

    public class PostMap : EntityTypeConfiguration<Post>
    {
        public PostMap()
        {
            this.HasKey(c => c.Id);
            this.ToTable("t_post");
            this.Property(c => c.Id).HasColumnName("PostId");
            this.Property(c => c.PostTitle).HasColumnName("Title");
        }
    }
}
复制代码

 

运行截图:

 

小计:优化后,经过测试,从原来的 循环60万次,耗时14秒,降低到 循环60万次,耗时 3.21 秒。

 

谢谢浏览!

点击右上角即可分享
微信分享提示