NopCommerce源码架构详解-EF相关Fluent API实现源码分析2

前面一篇文章介绍了nop中EF的Fluent API实现的一些基本类,其中最关键的就是Map映射下面我来介绍EF的Fluent API中的基本类和Map实体映射这些是在Nop中怎么调用和生效的。

NopCommerce源码架构详解概述

文章目录

概述

NopCommerce源码架构详解-EF相关Fluent API实现源码分析2。

内容

前面一篇文章介绍了nop中EF的Fluent API实现的一些基本类,其中最关键的就是Map映射下面我来介绍EF的Fluent API中的基本类和Map实体映射这些是在Nop中怎么调用和生效的。同样的我们先来看看核心的类图。

2020-04-23-17-49-56

主要类及作用:

1、NopEngine:Nop引擎注册所有的依赖(RegisterDependencies)及启动任务(RunStartupTasks),其中包括执行所有实现接口IStartupTask的类的Execute方法。

protected virtual void RunStartupTasks()
{
     var typeFinder = _containerManager.Resolve<ITypeFinder>();
     var startUpTaskTypes = typeFinder.FindClassesOfType<IStartupTask>();
     var startUpTasks = new List<IStartupTask>();
     foreach (var startUpTaskType in startUpTaskTypes)
      startUpTasks.Add((IStartupTask)Activator.CreateInstance(startUpTaskType));
     //sort
     startUpTasks = startUpTasks.AsQueryable().OrderBy(st => st.Order).ToList();
     foreach (var startUpTask in startUpTasks)
      startUpTask.Execute();
}

public void Initialize(NopConfig config)
{
     //register dependencies
     RegisterDependencies(config);

     //startup tasks
     if (!config.IgnoreStartupTasks)
     {
      RunStartupTasks();
     }
}

2、SqlServerDataProvider:初始化数据库连接工厂及设置DatabaseInitializer。

using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;
using System.IO;
using System.Text;
using System.Web.Hosting;
using Nop.Core.Data;
using Nop.Data.Initializers;

namespace Nop.Data
{
    public class SqlServerDataProvider : IDataProvider
    {
        #region Utilities

        protected virtual string[] ParseCommands(string filePath, bool throwExceptionIfNonExists)
        {
            if (!File.Exists(filePath))
            {
                if (throwExceptionIfNonExists)
                    throw new ArgumentException(string.Format("Specified file doesn't exist - {0}", filePath));
                else
                    return new string[0];
            }

            var statements = new List<string>();
            using (var stream = File.OpenRead(filePath))
            using (var reader = new StreamReader(stream))
            {
                var statement = "";
                while ((statement = ReadNextStatementFromStream(reader)) != null)
                {
                    statements.Add(statement);
                }
            }

            return statements.ToArray();
        }

        protected virtual string ReadNextStatementFromStream(StreamReader reader)
        {
            var sb = new StringBuilder();

            string lineOfText;

            while (true)
            {
                lineOfText = reader.ReadLine();
                if (lineOfText == null)
                {
                    if (sb.Length > 0)
                        return sb.ToString();
                    else
                        return null;
                }

                if (lineOfText.TrimEnd().ToUpper() == "GO")
                    break;

                sb.Append(lineOfText + Environment.NewLine);
            }

            return sb.ToString();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Initialize connection factory
        /// </summary>
        public virtual void InitConnectionFactory()
        {
            var connectionFactory = new SqlConnectionFactory();
            //TODO fix compilation warning (below)
            #pragma warning disable 0618
            Database.DefaultConnectionFactory = connectionFactory;
        }

        /// <summary>
        /// Initialize database
        /// </summary>
        public virtual void InitDatabase()
        {
            InitConnectionFactory();
            SetDatabaseInitializer();
        }

        /// <summary>
        /// Set database initializer
        /// </summary>
        public virtual void SetDatabaseInitializer()
        {
            //pass some table names to ensure that we have NopCommerce 2.X installed
            var tablesToValidate = new[] { "Customer", "Discount", "Order", "Product", "ShoppingCartItem" };

            //custom commands (stored proedures, indexes)

            var customCommands = new List<string>();
            //use webHelper.MapPath instead of HostingEnvironment.MapPath which is not available in unit tests
            customCommands.AddRange(ParseCommands(HostingEnvironment.MapPath("~/App_Data/SqlServer.Indexes.sql"), false));
            //use webHelper.MapPath instead of HostingEnvironment.MapPath which is not available in unit tests
            customCommands.AddRange(ParseCommands(HostingEnvironment.MapPath("~/App_Data/SqlServer.StoredProcedures.sql"), false));

            var initializer = new CreateTablesIfNotExist<NopObjectContext>(tablesToValidate, customCommands.ToArray());
            Database.SetInitializer(initializer);
        }

        /// <summary>
        /// A value indicating whether this data provider supports stored procedures
        /// </summary>
        public virtual bool StoredProceduredSupported
        {
            get { return true; }
        }

        /// <summary>
        /// Gets a support database parameter object (used by stored procedures)
        /// </summary>
        /// <returns>Parameter</returns>
        public virtual DbParameter GetParameter()
        {
            return new SqlParameter();
        }

        #endregion
    }
}

3、EfStartUpTask:执行所依赖对象IDataProvider的方法SetDatabaseInitializer,EF对应数据库Provider的初始化。

using Nop.Core;
using Nop.Core.Data;
using Nop.Core.Infrastructure;

namespace Nop.Data
{
    public class EfStartUpTask : IStartupTask
    {
        public void Execute()
        {
            var settings = EngineContext.Current.Resolve<DataSettings>();
            if (settings != null && settings.IsValid())
            {
                var provider = EngineContext.Current.Resolve<IDataProvider>();
                if (provider == null)
                    throw new NopException("No IDataProvider found");
                provider.SetDatabaseInitializer();
            }
        }

        public int Order
        {
            //ensure that this task is run first
            get { return -1000; }
        }
    }
}

4、EfDataProviderManager:一个IDataProvider的工厂类,可以根据配置返回接口IDataProvider的不同实现的具体类。如:SqlServerDataProvider,SqlCeDataProvider等等。

using System;
using Nop.Core;
using Nop.Core.Data;

namespace Nop.Data
{
    public partial class EfDataProviderManager : BaseDataProviderManager
    {
        public EfDataProviderManager(DataSettings settings):base(settings)
        {
        }

        public override IDataProvider LoadDataProvider()
        {

            var providerName = Settings.DataProvider;
            if (String.IsNullOrWhiteSpace(providerName))
                throw new NopException("Data Settings doesn't contain a providerName");

            switch (providerName.ToLowerInvariant())
            {
                case "sqlserver":
                    return new SqlServerDataProvider();
                case "sqlce":
                    return new SqlCeDataProvider();
                default:
                    throw new NopException(string.Format("Not supported dataprovider name: {0}", providerName));
            }
        }

    }
}

5、NopObjectContext:这相类继承于DbContext, IDbContext通过重写OnModelCreating方法通过反射获取AppDomain下面所有的程序集,然后动态创建EntityTypeConfiguration的实例,通过EF提供的API把我们配置的所有实体Map映射加入到modelBuilder的配置中去。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration;
using System.linq;
using System.Reflection;
using Nop.Core;

namespace Nop.Data
{
    public class NopObjectContext : DbContext, IDbContext
    {
        #region Ctor

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

        #endregion

        #region Utilities

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
        //获取AppDomain下面所有的程序集
            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)
            {
        //动态创建EntityTypeConfiguration的实例,并把我们配置的实体Map映射加入到modelBuilder的配置中去。
                dynamic configurationInstance = Activator.CreateInstance(type);
                modelBuilder.Configurations.Add(configurationInstance);
            }
            //...or do it manually below. For example,
            //modelBuilder.Configurations.Add(new LanguageMap());

            base.OnModelCreating(modelBuilder);
        }

        /// <summary>
        /// Attach an entity to the context or return an already attached entity (if it was already attached)
        /// </summary>
        /// <typeparam name="TEntity">TEntity</typeparam>
        /// <param name="entity">Entity</param>
        /// <returns>Attached entity</returns>
        protected virtual TEntity AttachEntityToContext<TEntity>(TEntity entity) where TEntity : BaseEntity, new()
        {
            //little hack here until Entity Framework really supports stored procedures
            //otherwise, navigation properties of loaded entities are not loaded until an entity is attached to the context
            var alreadyAttached = Set<TEntity>().Local.FirstOrDefault(x => x.Id == entity.Id);
            if (alreadyAttached == null)
            {
                //attach new entity
                Set<TEntity>().Attach(entity);
                return entity;
            }
            else
            {
                //entity is already loaded.
                return alreadyAttached;
            }
        }

        #endregion

        //...其实的一些共用方法
    }
}

6、DependencyRegistrar:依赖注入相关类的注册,其中就包括了IDbContext用类NopObjectContext来注册。

Nop.Web.Framework.DependencyRegistrar : IDependencyRegistrar
public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
{
    //....
    builder.Register(x => x.Resolve<BaseDataProviderManager>().LoadDataProvider()).As<IDataProvider>().InstancePerDependency();

    if (dataProviderSettings != null && dataProviderSettings.IsValid())
    {
        var efDataProviderManager = new EfDataProviderManager(dataSettingsManager.LoadSettings());
        var dataProvider = efDataProviderManager.LoadDataProvider();
        dataProvider.InitConnectionFactory();

        builder.Register<IDbContext>(c => new NopObjectContext(dataProviderSettings.DataConnectionString)).InstancePerLifetimeScope();
    }
    else
    {
        builder.Register<IDbContext>(c => new NopObjectContext(dataSettingsManager.LoadSettings().DataConnectionString)).InstancePerLifetimeScope();
    }
}
原文出处:蓝狐软件工作室【蓝狐】

原文链接:http://m.lanhusoft.com/Article/349.html

本文观点不代表 .Net中文网 立场,转载请联系原作者。

发表评论

登录后才能评论