文章目录
概述
NopCommerce源码架构详解-EF相关Fluent API实现源码分析2。
内容
前面一篇文章介绍了nop中EF的Fluent API实现的一些基本类,其中最关键的就是Map映射下面我来介绍EF的Fluent API中的基本类和Map实体映射这些是在Nop中怎么调用和生效的。同样的我们先来看看核心的类图。

主要类及作用:
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(); } }