1. 程式人生 > >.Net框架搭建之2、SQL Server MEF依賴注入 MVC Repository框架

.Net框架搭建之2、SQL Server MEF依賴注入 MVC Repository框架

MEF依賴注入簡介

依賴注入對於開發人員來說,方便的就是不需要去關注具體的實現類,不需要去New例項物件,直接使用介面層就能讓程式自動注入使用,當然,還有其他一些特點,比如web http同一個請求中可以設定同一個物件只例項化一次解決多個類中多次例項化物件浪費資源的問題。不多說,百度能得到更多 多的介紹,這邊直接開工搭環境。

1、資料模型Model層建立

資料模型層,首先要建立資料庫,再建立Model類。
建立資料庫,表,新增一條測試資料

建立資料庫

建立資料庫

建立資料表

建立資料表

新增測試資料

加測試資料

我們已經知道有幾層,所以,先把所有的類庫專案全部先建立好,web為MVC的空專案,至於各層程式碼,分到各層再去處理

專案結構

專案結構

各層依賴關係

各層依賴關係

好了,一般情況下,在這個框架裡,我們只需要建立相應的Model類與資料庫表相對應就行了,比如這個專案,我們只建立兩個類:

SysUser.cs

/* ==============================================================================
 * 功能描述:SysUserInfo  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 15:58:13
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InjectExample.Model { public partial class SysUser { public long ID { get; set; } public string Name { get; set; } public int Age { get; set
; } public string Remark { get; set; } } }

SysUserInfo.cs

/* ==============================================================================
 * 功能描述:SysUserInfo  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 15:58:13
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InjectExample.Model
{
    public partial class SysUserInfo
    {
        public long ID { get; set; }
        public long SysUserId { get; set; }
        public int LoginCount { get; set; }
        public DateTime LastLoginTime { get; set; }
    }
}

至此,Model類就建立完成。

2、底層公共方法層Component處理

這層可以說是在這個框架搭建中最複雜的,但是,建立完之後,不需要怎麼改動的地方。
好,為了能滿足框架程式碼需要引用的Nuget包,我這裡列出一下。

2.1 新增Nuget包和 .Net程式集引用

  <package id="EntityFramework" version="6.1.3" targetFramework="net45" />
  <package id="EntityFramework.Extended" version="6.1.0.168" targetFramework="net45" />
  <package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" />
  <package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net45" />
  <package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net45" />
  <package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net45" />
  <package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.3" targetFramework="net45" />
  <package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net45" />
  <package id="Microsoft.Composition" version="1.0.30" targetFramework="net45" />
  <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
  <package id="Newtonsoft.Json" version="6.0.4" targetFramework="net45" /> 

在nuget管理器中新增完成就行。
nuget包安裝
所有專案都勾上,免得麻煩。

還有一些,要新增系統的程式集,比如 System.Web,System.Configuration等等。
新增系統的程式集

2.2 建立mvc依賴注入相關類:

依賴注入相關類列表

先新增一個擴充套件類ConfigurationExt.cs,用來載入bin目錄下的所有程式集

/* ==============================================================================
 * 功能描述:Configuration  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 10:47:49
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Composition.Hosting;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace InjectExample.Component
{
    public static class ConfigurationExt
    {
        /// <summary>
        /// 或取應用程式 Bin 路徑
        /// </summary>
        /// <param name="inWeb">是否 web 應用</param>
        /// <returns></returns>
        public static IList<Assembly> DefaultAssemblies(bool inWeb)
        {
            var exts = new[] { "exe", "dll" };
            var dir = inWeb ? HttpRuntime.BinDirectory : AppDomain.CurrentDomain.BaseDirectory;
            var ns = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace;//獲取本類的名稱空間
            var nsStrs = ns.Split(new char[] { '.' });//拆分要取名稱空間字首
            var files = Directory.EnumerateFiles(dir, nsStrs[0] + "*", SearchOption.TopDirectoryOnly)
                .Where(file => exts.Any(x => file.EndsWith(x, StringComparison.OrdinalIgnoreCase)))
                .ToList();

            return files.Select(Assembly.LoadFrom).ToList();
        }

        public static ContainerConfiguration WithDefaultAssemblies(this ContainerConfiguration configuration)
        {
            configuration.WithAssemblies(DefaultAssemblies(true));
            return configuration;
        }
        public static bool IsInNamespace(this Type type, string namespaceFragment)
        {
            return type.Namespace != null && (type.Namespace.EndsWith("." + namespaceFragment) || type.Namespace.Contains("." + namespaceFragment + "."));
        }
    }
}

CompositionProvider.cs

/* ==============================================================================
 * 功能描述:CompositionProvider  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 10:33:07
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Composition;
using System.Composition.Hosting;
using System.Composition.Hosting.Core;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.ModelBinding;
using System.Web.Mvc;

namespace InjectExample.Component.Mvc
{
    /// <summary>
    /// Provides composition services to ASP.NET MVC by integrating DependencyResolver with
    /// the Managed Extensibility Framework (MEF). This class is self-configuring and will be
    /// enabled by simply being present in the application's Bin directory. Most applications
    /// should not need to access this class.
    /// </summary>
    public static class CompositionProvider
    {
        static CompositionHost _container;
        static ExportFactory<CompositionContext> _requestScopeFactory;
        static IList<Assembly> _partAssemblies = new List<Assembly>();

        /// <summary>
        /// Used to override the default conventions for controller/part dependency injection.
        /// Cannot be used in conjunction with any other methods on this type. Most applications
        /// should not use this method.
        /// </summary>
        /// <param name="configuration">A configuration containing the controller types and other parts that
        /// should be used by the composition provider.</param>
        public static void SetConfiguration(ContainerConfiguration configuration)
        {
            if (configuration == null) throw new ArgumentNullException("configuration");
            if (IsInitialized) throw new InvalidOperationException("Already initialized.");

            // We add RSF with no conventions (overriding anything set as the default in configuration)
            _container = configuration.CreateContainer();

            var factoryContract = new CompositionContract(
                typeof(ExportFactory<CompositionContext>),
                null,
                new Dictionary<string, object> { { "SharingBoundaryNames", new[] { Boundaries.HttpRequest, Boundaries.DataConsistency, Boundaries.UserIdentity } } }
                );

            _requestScopeFactory = (ExportFactory<CompositionContext>)_container.GetExport(factoryContract);

            ConfigureMvc();
            //ConfigureWebApi();
        }

        //static void ConfigureWebApi()
        //{
        //    System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new ScopeHttpDependencyResolver();
        //}

        static void ConfigureMvc()
        {
            if (DependencyResolver.Current.GetType().Name != "DefaultDependencyResolver")
            {
                throw new CompositionFailedException("MVC's DependencyResolver has been changed.");
            }

            DependencyResolver.SetResolver(new ScopeDependencyResolver());

            FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().SingleOrDefault());
            FilterProviders.Providers.Add(new ScopeFilterAttributeFilterProvider());

            System.Web.Mvc.ModelBinderProviders.BinderProviders.Add(new ScopeModelBinderProvider());
        }

        public static Export<CompositionContext> CreateContext()
        {
            PostStartDefaultInitialize();
            return _requestScopeFactory.CreateExport();
        }

        internal static CompositionContext Current
        {
            get
            {
                var current = CurrentInitialisedScope;
                if (current == null)
                {
                    current = _requestScopeFactory.CreateExport();
                    CurrentInitialisedScope = current;
                }
                return current.Value;
            }
        }

        internal static Export<CompositionContext> CurrentInitialisedScope
        {
            get { return (Export<CompositionContext>)HttpContext.Current.Items[typeof(CompositionProvider)]; }
            private set { HttpContext.Current.Items[typeof(CompositionProvider)] = value; }
        }

        /// <summary>
        /// Adds assemblies containing MEF parts to the composition provider.
        /// </summary>
        /// <param name="assemblies">Assemblies containing parts to add.</param>
        public static void AddAssemblies(params Assembly[] assemblies)
        {
            AddAssemblies((IEnumerable<Assembly>)assemblies);
        }

        public static void AddAssemblies(IEnumerable<Assembly> assemblies)
        {
            if (assemblies == null) throw new ArgumentException("assemblies");

            foreach (var assembly in assemblies)
                AddAssembly(assembly);
        }

        public static void AddAssembly(Assembly assembly)
        {
            if (assembly == null) throw new ArgumentNullException("assembly");

            _partAssemblies.Add(assembly);
        }

        internal static void PostStartDefaultInitialize()
        {
            if (!IsInitialized)
            {
                SetConfiguration(new MvcContainerConfiguration());
            }
        }

        static bool IsInitialized
        {
            get { return _requestScopeFactory != null; }
        }
    }
}

CompositionScopeModule.cs

using Microsoft.Web.Infrastructure.DynamicModuleHelper;
/* ==============================================================================
 * 功能描述:CompositionScopeModule  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 11:53:26
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace InjectExample.Component.Mvc
{
    /// <summary>
    /// Provides lifetime management for the <see cref="CompositionProvider"/> type.
    /// This module is automatically injected into the ASP.NET request processing
    /// pipeline at startup and should not be called by user code.
    /// </summary>
    public class CompositionScopeModule : IHttpModule
    {
        static bool _isInitialized;

        /// <summary>
        /// Register the module. This method is automatically called
        /// at startup and should not be called by user code.
        /// </summary>
        public static void Register()
        {
            if (!_isInitialized)
            {
                _isInitialized = true;
                DynamicModuleUtility.RegisterModule(typeof(CompositionScopeModule));
            }
        }

        /// <summary>
        /// Release resources used by the module.
        /// </summary>
        public void Dispose()
        {
        }

        /// <summary>
        /// Initialize the module.
        /// </summary>
        /// <param name="context">Application in which the module is running.</param>
        public void Init(HttpApplication context)
        {
            context.EndRequest += DisposeCompositionScope;

            CompositionProvider.PostStartDefaultInitialize();
        }

        static void DisposeCompositionScope(object sender, EventArgs e)
        {
            var scope = CompositionProvider.CurrentInitialisedScope;
            if (scope != null)
                scope.Dispose();
        }
    }
}

ExportModelBinderAttribute.cs

/* ==============================================================================
 * 功能描述:ExportModelBinderAttribute  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 11:55:36
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace InjectExample.Component.Mvc
{
    /// <summary>
    /// Marks a type as being a model binder for a specified model type. The type decorated with
    /// this attribute must implement the <see cref="IModelBinder"/> interface.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field)]
    public sealed class ExportModelBinderAttribute : ExportAttribute
    {
        public ExportModelBinderAttribute(Type modelType)
            : base(ScopeModelBinderProvider.GetModelBinderContractName(modelType), typeof(IModelBinder))
        {
        }
    }
}

MvcContainerConfiguration.cs

/* ==============================================================================
 * 功能描述:MvcContainerConfiguration  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 10:46:21
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Composition.Convention;
using System.Composition.Hosting;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Mvc;

namespace InjectExample.Component.Mvc
{
    /// <summary>
    /// A <see cref="ContainerConfiguration"/> that loads parts in the currently-executing ASP.NET MVC web application.
    /// Parts are detected by namespace - classes in a ".Parts" namespace will be considered to be parts. These classes,
    /// and any interfaces they implement, will be exported and shared on a per-HTTP-request basis. When resolving
    /// dependencies, the longest public constructor on a part type will be used. The <see cref="ImportAttribute"/>,
    /// <see cref="ExportAttribute"/> and associated MEF attributes can be applied to modify composition structure.
    /// </summary>
    /// <remarks>
    /// This implementation emulates CompositionContainer and the composition-container based MVC
    /// integration for all types under the Parts namespace, for controllers, and for model binders. This will
    /// aid migration from one composition engine to the other, but this decision should be revisited if it
    /// causes confusion.
    /// </remarks>
    public class MvcContainerConfiguration : ContainerConfiguration
    {
        MvcContainerConfiguration(IEnumerable<Assembly> assemblies, AttributedModelProvider reflectionContext)
        {
            if (assemblies == null) throw new ArgumentNullException("assemblies");
            if (reflectionContext == null) throw new ArgumentNullException("reflectionContext");

            this.WithDefaultConventions(reflectionContext);
            this.WithAssemblies(assemblies);
        }

        /// <summary>
        /// Construct an <see cref="MvcContainerConfiguration"/> using parts in the specified assemblies.
        /// </summary>
        /// <param name="assemblies">Assemblies in which to search for parts.</param>
        public MvcContainerConfiguration(IEnumerable<Assembly> assemblies)
            : this(assemblies, DefineConventions())
        {
        }

        /// <summary>
        /// Construct an <see cref="MvcContainerConfiguration"/> using parts in the main application assembly.
        /// In some applications this may not be the expected assembly - in those cases specify the
        /// assemblies explicitly using the other constructor.
        /// </summary>
        public MvcContainerConfiguration()
            : this(ConfigurationExt.DefaultAssemblies(true))
        {
        }

        internal static Assembly GuessGlobalApplicationAssembly()
        {
            return HttpContext.Current.ApplicationInstance.GetType().BaseType.Assembly;
        }

        private static AttributedModelProvider DefineConventions()
        {
            var rb = new ConventionBuilder();

            rb.ForTypesDerivedFrom<IController>().Export();

            rb.ForTypesDerivedFrom<IHttpController>().Export();

            rb.ForTypesMatching(IsAPart)
                .Export()
                .ExportInterfaces();

            return rb;
        }

        private static bool IsAPart(Type t)
        {
            return !t.IsAssignableFrom(typeof(Attribute)) && t.IsInNamespace("Parts");
        }
    }
}

RemoveHttpHeadModule.cs,這個和依賴注入沒關係,是附加的功能

/* ==============================================================================
 * 功能描述:RemoveHttpHeadModule  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 11:57:06
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace InjectExample.Component.Mvc
{
    /// <summary>
    /// 移除Server HTTP頭
    /// </summary>
    public class RemoveHttpHeadModule : IHttpModule
    {
        void context_PreSendRequestHeaders(object sender, EventArgs e)
        {
            HttpContext.Current.Response.Headers.Remove("Server");
        }

        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public void Init(HttpApplication context)
        {
            //移除Server HTTP頭
            context.PreSendRequestHeaders += context_PreSendRequestHeaders;
        }
    }
}

ScopeDependencyResolver.cs

/* ==============================================================================
 * 功能描述:ScopeDependencyResolver  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 10:39:59
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace InjectExample.Component.Mvc
{
    public class ScopeDependencyResolver : IDependencyResolver
    {
        public object GetService(Type serviceType)
        {
            object export;
            try
            {
                if (!CompositionProvider.Current.TryGetExport(serviceType, null, out export))
                    return null;
            }
            catch
            {
                return null;
            }


            return export;
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return CompositionProvider.Current.GetExports(serviceType);
        }
    }
}

ScopeFilterAttributeFilterProvider.cs

/* ==============================================================================
 * 功能描述:ScopeFilterAttributeFilterProvider  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 10:40:54
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;
using System.Composition;

namespace InjectExample.Component.Mvc
{
    class ScopeFilterAttributeFilterProvider : FilterAttributeFilterProvider
    {
        public ScopeFilterAttributeFilterProvider()
            : base(cacheAttributeInstances: false) { }

        protected override IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            var attributes = base.GetActionAttributes(controllerContext, actionDescriptor).ToArray();
            ComposeAttributes(attributes);
            return attributes;
        }

        protected override IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            var attributes = base.GetControllerAttributes(controllerContext, actionDescriptor).ToArray();
            ComposeAttributes(attributes);
            return attributes;
        }

        void ComposeAttributes(FilterAttribute[] attributes)
        {
            foreach (var attribute in attributes)
                CompositionProvider.Current.SatisfyImports(attribute);
        }
    }
}

ScopeModelBinderProvider.cs

/* ==============================================================================
 * 功能描述:ScopeModelBinderProvider  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 10:45:30
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace InjectExample.Component.Mvc
{
    class ScopeModelBinderProvider : IModelBinderProvider
    {
        const string ModelBinderContractNameSuffix = "++ModelBinder";

        public static string GetModelBinderContractName(Type modelType)
        {
            return modelType.AssemblyQualifiedName + ModelBinderContractNameSuffix;
        }

        public IModelBinder GetBinder(Type modelType)
        {
            IModelBinder export;
            if (!CompositionProvider.Current.TryGetExport(GetModelBinderContractName(modelType), out export))
                return null;

            return export;
        }
    }
}

2.3 建立EF自定義實體對映相關底層類

主要是一些基層的單元操作、Repository公共介面和公共實現、DbContext類、資料庫檢視初始化等等。
自定義實體對映相關底層類

DatabaseInitializer.cs

using InjectExample.Component.Mvc;
/* ==============================================================================
 * 功能描述:DatabaseInitializer  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 10:28:48
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
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.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InjectExample.Component
{
    /// <summary>
    /// 資料庫初始化操作類
    /// </summary>
    public static class DatabaseInitializer
    {
        /// <summary>
        /// 資料庫初始化
        /// </summary>
        public static void Initialize()
        {
            //關閉初始化 
            Database.SetInitializer<EfDbContext>(null);

        }

        /// <summary>
        /// EF 預生成檢視
        /// </summary>
        public static void PreCreateView()
        {
            using (var container = CompositionProvider.CreateContext())
            {
                var context = container.Value.GetExport<EfDbContext>();
                var objectContext = ((IObjectContextAdapter)context).ObjectContext;
                var collection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
                collection.GenerateViews(new List<EdmSchemaError>());
            }
        }
    }
}

EfDbContext.cs

/* ==============================================================================
 * 功能描述:EfDbContext  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 10:42:54
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Composition;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InjectExample.Component
{
    /// <summary>
    ///     EF資料訪問上下文
    /// </summary>
    [Shared(Boundaries.DataConsistency)]
    [Export(typeof(EfDbContext))]
    public class EfDbContext : DbContext
    {
        public EfDbContext()
            : base("InjextExampleEntities")//連線字串名稱
        {
            Configuration.LazyLoadingEnabled = false;
            Configuration.ProxyCreationEnabled = false;
            Configuration.ValidateOnSaveEnabled = false;
        }
        /// <summary>
        /// 獲取實現了IEntityMapper介面的entity mappers,用來註冊到Db上下文
        /// </summary>
        [ImportMany]
        public IEnumerable<IEntityMapper> EntityMappers { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            Configuration.ProxyCreationEnabled = false;
            Configuration.LazyLoadingEnabled = false;
            //Configuration.AutoDetectChangesEnabled = false;
            //去掉一對多關係
            modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

            //去除生成表名複數
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            //防止黑幕交易 要不然每次都要訪問 (如果之前已經有資料庫了,那麼再加上移除對EdmMetadata表訪問的配置再跑程式會報一個NotSupportedException的錯)
            //modelBuilder.Conventions.Remove<IncludeMetadataConvention>();

            if (EntityMappers == null)
            {
                return;
            }

            foreach (var mapper in EntityMappers)
            {
                mapper.RegistTo(modelBuilder.Configurations);
            }

            base.OnModelCreating(modelBuilder);
        }

    }
}

EFRepositoryBase.cs

using InjectExample.Model.ViewModel;
/* ==============================================================================
 * 功能描述:EFRepositoryBase  
 * 創 建 者:蒲奎民
 * 建立日期:2016-08-29 10:56:35
 * CLR Version :4.0.30319.42000
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Composition;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using EntityFramework.Extensions;

namespace InjectExample.Component
{
    /// <summary>
    ///     EntityFramework倉儲操作基類
    /// </summary>
    /// <typeparam name="TEntity">動態實體型別</typeparam>
    public abstract class EfRepositoryBase<TEntity> : IRepository<TEntity> where TEntity : class //EntityBase<TKey>
    {
        #region 屬性

        /// <summary>
        ///     獲取 倉儲上下文的例項
        /// </summary>
        [Import]
        public IUnitOfWork UnitOfWork { get; set; }

        /// <summary>
        ///     獲取 EntityFramework的資料倉儲上下文
        /// </summary>
        protected UnitOfWorkContextBase EfContext
        {
            get
            {
                if (UnitOfWork is UnitOfWorkContextBase)
                {
                    return UnitOfWork as UnitOfWorkContextBase;
                }
                return null;
                // throw new DataAccessException(string.Format("資料倉儲上下文物件型別不正確,應為UnitOfWorkContextBase,實際為 {0}", UnitOfWork.GetType().Name));
            }
        }

        /// <summary>
        ///  獲取 當前實體的查詢資料集,不跟蹤狀態
        /// </summary>
        public virtual IQueryable<TEntity> Entities
        {
            get { return EfContext.Set<TEntity>().AsNoTracking(); }
        }
        /// <summary>
        /// 獲取 當前實體的查詢資料集,有跟蹤狀態
        /// </summary>
        public virtual IQueryable<TEntity> Table
        {
            get { return EfContext.Set<TEntity>(); }
        }

        #endregion

        #region 公共方法

        /// <summary>
        ///     插入實體記錄
        /// </summary>
        /// <param name="entity"> 實體物件 </param>
        /// <param name="isSave"> 是否執行儲存 </param>
        /// <returns> 操作影響的行數 </returns>
        public virtual int Insert(TEntity entity, bool isSave = true)
        {
            PublicHelper.CheckArgument(entity, "entity");
            EfContext.RegisterNew(entity);
            return isSave ? EfContext.Commit() : 0;
        }

        /// <summary>
        ///     批量插入實體記錄集合
        /// </summary>
        /// <param name="entities"> 實體記錄集合 </param>
        /// <param name="isSave"> 是否執行儲存 </param>
        /// <returns> 操作影響的行數 </returns>
        public virtual int Insert(IEnumerable<TEntity> entities, bool isSave = true)
        {
            PublicHelper.CheckArgument(entities, "entities")