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; }
}
}
新建类时,里面会自动加入创建者信息,怎么加的,可参考:
VS2013修改模版、创建类模版文件参考:
http://blog.csdn.net/pukuimin1226/article/details/51685279
至此,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管理器中添加完成就行。
所有项目都勾上,免得麻烦。
还有一些,要添加系统的程序集,比如 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");
EfContext.RegisterNew(entities);
return isSave ? EfContext.Commit() : 0;
}
///// <summary>
///// 删除指定编号的记录
///// </summary>
///// <param name="id"> 实体记录编号 </param>
///// <param name="isSave"> 是否执行保存 </param>
///// <returns> 操作影响的行数 </returns>
//public virtual int Delete(object id, bool isSave = true)
//{
// PublicHelper.CheckArgument(id, "id");
// TEntity entity = EFContext.Set<TEntity>().Find(id);
// return entity != null ? Delete(entity, isSave) : 0;
//}
/// <summary>
/// 删除实体记录
/// </summary>
/// <param name="entity"> 实体对象 </param>
/// <param name="isSave"> 是否执行保存 </param>
/// <returns> 操作影响的行数 </returns>
public virtual int Delete(TEntity entity, bool isSave = true)
{
PublicHelper.CheckArgument(entity, "entity");
EfContext.RegisterDeleted<TEntity>(entity);
return isSave ? EfContext.Commit() : 0;
}
/// <summary>
/// 删除实体记录集合
/// </summary>
/// <param name="entities"> 实体记录集合 </param>
/// <param name="isSave"> 是否执行保存 </param>
/// <returns> 操作影响的行数 </returns>
public virtual int Delete(IEnumerable<TEntity> entities, bool isSave = true)
{
PublicHelper.CheckArgument(entities, "entities");
EfContext.RegisterDeleted<TEntity>(entities);
return isSave ? EfContext.Commit() : 0;
}
/// <summary>
/// 删除所有符合特定表达式的数据
/// </summary>
/// <param name="predicate"> 查询条件谓语表达式 </param>
/// <param name="isSave"> 是否执行保存 </param>
/// <returns> 操作影响的行数 </returns>
public virtual int Delete(Expression<Func<TEntity, bool>> predicate, bool isSave = true)
{
PublicHelper.CheckArgument(predicate, "predicate");
List<TEntity> entities = EfContext.Set<TEntity>().Where(predicate).ToList();
return entities.Count > 0 ? Delete(entities, isSave) : 0;
}
/// <summary>
/// 更新实体记录
/// </summary>
/// <param name="entity"> 实体对象 </param>
/// <param name="isSave"> 是否执行保存 </param>
/// <returns> 操作影响的行数 </returns>
public virtual int Update(TEntity entity, bool isSave = true)
{
PublicHelper.CheckArgument(entity, "entity");
EfContext.RegisterModified<TEntity>(entity);
return isSave ? EfContext.Commit() : 0;
}
public virtual int Update(IEnumerable<TEntity> entities, bool isSave = true)
{
PublicHelper.CheckArgument(entities, "entities");
EfContext.RegisterModified<TEntity>(entities);
return isSave ? EfContext.Commit() : 0;
}
/// <summary>
/// 提交更新
/// </summary>
/// <returns></returns>
public virtual int Commit(bool validateOnSaveEnabled = true)
{
return EfContext.Commit(validateOnSaveEnabled);
}
/// <summary>
/// 查找指定主键的实体记录
/// </summary>
/// <param name="key"> 指定主键 </param>
/// <returns> 符合编号的记录,不存在返回null </returns>
public virtual TEntity GetByKey(object key)
{
PublicHelper.CheckArgument(key, "key");
return EfContext.Set<TEntity>().Find(key);
}
/// <summary>
/// 根据条件获取单条实体数据
/// </summary>
/// <param name="filter"></param>
/// <returns></returns>
public TEntity GetByFiltered(Expression<Func<TEntity, bool>> filter, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
{
var query = EfContext.Set<TEntity>().Where(filter);
if (orderBy != null)
{
query = orderBy(query);
}
HandleInclude(ref query, includeProperties);
return query.FirstOrDefault();
}
/// <summary>
/// 删除所有符合特定表达式的数据
/// </summary>
/// <param name="predicate"> 查询条件谓语表达式 </param>
/// <returns> 操作影响的行数 </returns>
public virtual int Delete(Expression<Func<TEntity, bool>> predicate)
{
return Entities.Where(predicate).Delete();
}
/// <summary>
/// 修改操作
/// </summary>
/// <param name="funWhere">查询条件-谓语表达式</param>
/// <param name="funUpdate">实体-谓语表达式</param>
/// <returns>操作影响的行数</returns>
public virtual int Update(Expression<Func<TEntity, bool>> funWhere, Expression<Func<TEntity, TEntity>> funUpdate)
{
return Entities.Where(funWhere).Update(funUpdate);
}
/// <summary>
/// 分页查询
/// </summary>
/// <param name="queryable"></param>
/// <param name="pageInfo">分页相关信息</param>
/// <param name="total">总记录数</param>
/// <returns></returns>
[Obsolete]
public virtual IList<dynamic> GetListByPage(IQueryable<dynamic> queryable, PagingInfo pageInfo, out int total)
{
// 获取总数
queryable = queryable.AsNoTracking();
var q1 = queryable.FutureCount();
// 获取分页的数据
var lsitTemp = queryable.Skip(pageInfo.PageIndex).Take(pageInfo.PageSize).Future();
// 这里会触发上面所有Future函数中的查询包装到一个连接中执行
total = q1.Value;
//因为已经得到结果了,这里不会再次查询
return lsitTemp.ToList();
}
/// <summary>
/// 分页查询
/// </summary>
/// <param name="filter">where条件</param>
/// <param name="orderBy">分页信息,会返回查询后的总条数(字段RecCount)</param>
/// <param name="pageInfo">分页信息,字段RecCount返回总条数,可以不分页,NeedPage=false即可不分页</param>
/// <param name="includeProperties">include的关联实体s,多个用逗号隔开</param>
/// <returns></returns>
public IQueryable<TEntity> GetListByPage(Expression<Func<TEntity, bool>> filter, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy, PagingInfo pageInfo, string includeProperties = "")
{
//纠正分页参数
pageInfo = pageInfo == null ? new PagingInfo { PageIndex = 1, PageSize = 10 } : pageInfo;
pageInfo.PageSize = pageInfo.PageSize > 0 ? pageInfo.PageSize : 10;
IQueryable<TEntity> query = filter != null ? Entities.Where(filter) : Entities;
HandleInclude(ref query, includeProperties);
pageInfo.RecCount = query.Count();
query = orderBy != null ? orderBy(query) : query;
return pageInfo.NeedPage ? (orderBy != null ? query.Skip((pageInfo.PageIndex - 1) * pageInfo.PageSize).Take(pageInfo.PageSize) : null) : query;
}
/// <summary>
/// 执行非查询sql语句
/// </summary>
/// <param name="sql">sql语句</param>
/// <param name="behavior">事务属性</param>
/// <param name="isInsertIdentity">sql语句是否在自增字段插入值</param>
/// <param name="tableName">操作的表名(可空)</param>
/// <param name="parameters">参数列表</param>
/// <returns>返回影响的行数</returns>
public virtual int ExcuteNoQuery(string sql, TransactionalBehavior behavior = TransactionalBehavior.DoNotEnsureTransaction, bool isInsertIdentity = false, string tableName = "", params object[] parameters)
{
if (isInsertIdentity)
{
if (tableName == "") tableName = typeof(TEntity).Name;
if (sql.TrimEnd().EndsWith(";") == false) sql = sql + ";";
sql = "SET IDENTITY_INSERT " + tableName + " ON;" + sql + "SET IDENTITY_INSERT " + tableName + " OFF;";
}
return EfContext.DbContext.Database.ExecuteSqlCommand(behavior, sql, parameters);
}
/// <summary>
/// 执行查询sql语句
/// </summary>
/// <typeparam name="T">返回类型</typeparam>
/// <param name="sql">sql语句</param>
/// <param name="parameters">参数列表</param>
/// <returns></returns>
public virtual List<T> ExcuteQuery<T>(string sql, params object[] parameters)
{
return EfContext.DbContext.Database.SqlQueryWithNoLock<T>(sql, parameters).ToList();
}
#endregion
private void HandleInclude(ref IQueryable<TEntity> query, string includeProperties)
{
if (!string.IsNullOrWhiteSpace(includeProperties))
{
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
}
}
public bool RemoveHoldingEntityInContext(TEntity entity)
{
var objContext = ((IObjectContextAdapter)EfContext.DbContext).ObjectContext;
var objSet = objContext.CreateObjectSet<TEntity>();
var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);
Object foundEntity;
var exists = objContext.TryGetObjectByKey(entityKey, out foundEntity);
if (exists)
{
objContext.Detach(foundEntity);
}
return (exists);
}
public void RemoveFromContext(TEntity entity)
{
PublicHelper.CheckArgument(entity, "entity");
EfContext.RegisterRemove(entity);
}
/// <summary>
/// 实体修改日志(不含导航属性)
/// T 必须有主键
/// 不建议循环调用
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="model"></param>
/// <returns></returns>
public string GetEntityPropertiesChangedLog(TEntity model)
{
var propertiesChangedLog = string.Empty;
var list = GetEntityPropertiesChanges(model);
foreach (var tuple in list)
{
var displayName = tuple.Item1.Name;
var displayAttribute = tuple.Item1.GetCustomAttribute(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute), false) as System.ComponentModel.DataAnnotations.DisplayAttribute;
if (displayAttribute != null && !string.IsNullOrEmpty(displayAttribute.Name))
{
displayName = displayAttribute.Name;
}
propertiesChangedLog += string.Format("{0}:{1}->{2};", displayName, tuple.Item2, tuple.Item3);
}
return propertiesChangedLog.TrimEnd(';');
}
public List<Tuple<PropertyInfo, string, string>> GetEntityPropertiesChanges(TEntity model)
{
var result = new List<Tuple<PropertyInfo, string, string>>();
#region 修改的字段
var detachFlag = false;
var propertiesChangedLog = string.Empty;
var entry = EfContext.DbContext.Entry(model);
if (entry.State == EntityState.Detached)
{
detachFlag = true;
var keyNames = ((IObjectContextAdapter)EfContext.DbContext).ObjectContext.CreateObjectSet<TEntity>().EntitySet.ElementType.KeyMembers.Select(k => k.Name);
var propertyValues = new List<object>();
foreach (var p in model.GetType().GetProperties().Where(m => keyNames.Contains(m.Name)).ToList())
{
propertyValues.Add(p.GetValue(model));
}
var dbModel = EfContext.Set<TEntity>().Find(propertyValues.ToArray());
entry = EfContext.DbContext.Entry(dbModel);
entry.CurrentValues.SetValues(model);
}
foreach (var propertyName in entry.CurrentValues.PropertyNames.Where(m => entry.Property(m).IsModified).ToList())
{
if (propertyName.StartsWith("Modified"))
continue;
var property = entry.Property(propertyName);
var displayName = propertyName;
var propetyInfo = model.GetType().GetProperty(propertyName);
result.Add(new Tuple<PropertyInfo, string, string>(propetyInfo, (property.OriginalValue == null ? string.Empty : JsonUtility.ToJson(property.OriginalValue)), (property.CurrentValue == null ? string.Empty : JsonUtility.ToJson(property.CurrentValue))));
}
if (detachFlag)
{
entry.State = EntityState.Detached;
}
#endregion
return result;
}
}
}
EFUnitOfWorkContext.cs
/* ==============================================================================
* 功能描述:EFUnitOfWorkContext
* 创 建 者:蒲奎民
* 创建日期:2016-08-29 11:02:52
* CLR Version :4.0.30319.42000
* ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Composition;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InjectExample.Component
{
/// <summary>
/// 数据单元操作类
/// </summary>
[Shared(Boundaries.DataConsistency)]
[Export(typeof(IUnitOfWork))]
public class EfUnitOfWorkContext : UnitOfWorkContextBase
{
//[Import]
// private ConnectionStringHelper ConnectionStringHelper { get; set; }
[Import]
public Lazy<EfDbContext> EfDbContext { get; set; }
//[Import("EFReport", typeof(DbContext))]
//private Lazy<EfReportDbContext> EfReportDbContext { get; set; }
/// <summary>
/// 获取 当前使用的数据访问上下文对象
/// </summary>
protected override DbContext Context
{
get
{
return EfDbContext.Value;
}
}
}
}
IEntityMapper.cs,公共接口
using System;
using System.Collections.Generic;
using System.Data.Entity.ModelConfiguration.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InjectExample.Component
{
/// <summary>
/// 实体映射接口
/// </summary>
public interface IEntityMapper
{
/// <summary>
/// 将当前实体映射对象注册到当前数据访问上下文实体映射配置注册器中
/// </summary>
/// <param name="configurations">实体映射配置注册器</param>
void RegistTo(ConfigurationRegistrar configurations);
}
}
IRepository.cs,公共接口
using InjectExample.Model.ViewModel;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace InjectExample.Component
{
/// <summary>
/// 定义仓储模型中的数据标准操作
/// </summary>
/// <typeparam name="TEntity">动态实体类型</typeparam>
public interface IRepository<TEntity>// where TEntity : class
{
#region 属性
/// <summary>
/// 获取 当前实体的查询数据集
/// </summary>
IQueryable<TEntity> Entities { get; }
IQueryable<TEntity> Table { get; }
#endregion
#region 公共方法
/// <summary>
/// 插入实体记录
/// </summary>
/// <param name="entity"> 实体对象 </param>
/// <param name="isSave"> 是否执行保存 </param>
/// <returns> 操作影响的行数 </returns>
int Insert(TEntity entity, bool isSave = true);
/// <summary>
/// 批量插入实体记录集合
/// </summary>
/// <param name="entities"> 实体记录集合 </param>
/// <param name="isSave"> 是否执行保存 </param>
/// <returns> 操作影响的行数 </returns>
int Insert(IEnumerable<TEntity> entities, bool isSave = true);
///// <summary>
///// 删除指定编号的记录
///// </summary>
///// <param name="id"> 实体记录编号 </param>
///// <param name="isSave"> 是否执行保存 </param>
///// <returns> 操作影响的行数 </returns>
//int Delete(object id, bool isSave = true);
/// <summary>
/// 删除实体记录
/// </summary>
/// <param name="entity"> 实体对象 </param>
/// <param name="isSave"> 是否执行保存 </param>
/// <returns> 操作影响的行数 </returns>
int Delete(TEntity entity, bool isSave = true);
/// <summary>
/// 删除实体记录集合
/// </summary>
/// <param name="entities"> 实体记录集合 </param>
/// <param name="isSave"> 是否执行保存 </param>
/// <returns> 操作影响的行数 </returns>
int Delete(IEnumerable<TEntity> entities, bool isSave = true);
/// <summary>
/// 删除所有符合特定表达式的数据
/// </summary>
/// <param name="predicate"> 查询条件谓语表达式 </param>
/// <param name="isSave"> 是否执行保存 </param>
/// <returns> 操作影响的行数 </returns>
int Delete(Expression<Func<TEntity, bool>> predicate, bool isSave = true);
/// <summary>
/// 更新实体记录
/// </summary>
/// <param name="entity"> 实体对象 </param>
/// <param name="isSave"> 是否执行保存 </param>
/// <returns> 操作影响的行数 </returns>
int Update(TEntity entity, bool isSave = true);
int Update(IEnumerable<TEntity> entities, bool isSave = true);
/// <summary>
/// 提交更新
/// </summary>
/// <returns></returns>
int Commit(bool validateOnSaveEnabled = true);
//int Update(Expression<Func<TEntity, object>> propertyExpression, TEntity entity, bool isSave = true);
/// <summary>
/// 使用附带新值的实体信息更新指定实体属性的值
/// </summary>
/// <returns>操作影响的行数</returns>
/// <summary>
/// 查找指定主键的实体记录(从缓存中获取)
/// </summary>
/// <param name="key"> 指定主键 </param>
/// <returns> 符合编号的记录,不存在返回null </returns>
///
TEntity GetByKey(object key);
/// <summary>
/// 通过条件获取单条实体记录
/// </summary>
/// <param name="filter"></param>
/// <returns></returns>
TEntity GetByFiltered(Expression<Func<TEntity, bool>> filter, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "");
/// <summary>
/// 删除所有符合特定表达式的数据
/// </summary>
/// <param name="predicate"> 查询条件谓语表达式 </param>
/// <returns> 操作影响的行数 </returns>
int Delete(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// 修改操作
/// </summary>
/// <param name="funWhere">查询条件-谓语表达式</param>
/// <param name="funUpdate">实体-谓语表达式</param>
/// <returns>操作影响的行数</returns>
int Update(Expression<Func<TEntity, bool>> funWhere, Expression<Func<TEntity, TEntity>> funUpdate);
/// <summary>
/// 分页查询
/// </summary>
/// <param name="source">查询集合</param>
/// <param name="pageInfo">分页相关信息</param>
/// <param name="total">总记录数</param>
/// <returns></returns>
[Obsolete]
IList<dynamic> GetListByPage(IQueryable<dynamic> source, PagingInfo pageInfo, out int total);
/// <summary>
/// 分页查询
/// </summary>
/// <param name="filter">where条件</param>
/// <param name="orderBy">分页信息,会返回查询后的总条数(字段RecCount)</param>
/// <param name="pageInfo">分页信息,字段RecCount返回总条数,可以不分页,NeedPage=false即可不分页</param>
/// <param name="includeProperties">include的关联实体s,多个用逗号隔开</param>
/// <returns></returns>
/// <returns></returns>
IQueryable<TEntity> GetListByPage(Expression<Func<TEntity, bool>> filter, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy, PagingInfo pageInfo, string includeProperties = "");
/// <summary>
/// 执行非查询sql语句
/// </summary>
/// <param name="sql">sql语句</param>
/// <param name="behavior">事务属性</param>
/// <param name="isInsertIdentity">sql语句是否在自增字段插入值</param>
/// <param name="tableName">操作的表名(可空)</param>
/// <param name="parameters">参数列表</param>
/// <returns>返回影响的行数</returns>
int ExcuteNoQuery(string sql, TransactionalBehavior behavior = TransactionalBehavior.DoNotEnsureTransaction, bool isInsertIdentity = false, string tableName = "", params object[] parameters);
List<T> ExcuteQuery<T>(string sql, params object[] parameters);
/// <summary>
/// 从上下文中脱离(detach)对象
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
bool RemoveHoldingEntityInContext(TEntity entity);
/// <summary>
/// 从上下文中移除(remove)对象
/// </summary>
/// <param name="entity"></param>
void RemoveFromContext(TEntity entity);
/// <summary>
/// 实体修改日志(不含导航属性)
/// T 必须有主键
/// 不建议循环调用
/// </summary>
/// <returns></returns>
string GetEntityPropertiesChangedLog(TEntity model);
/// <summary>
/// 获取实体属性改变内容
/// T 必须有主键
/// 不建议循环调用
/// </summary>
/// <param name="model"></param>
/// <returns>Item1 属性,Item2原值(Json),Item3新值(Json)</returns>
List<Tuple<PropertyInfo, string, string>> GetEntityPropertiesChanges(TEntity model);
#endregion
}
}
IUnitOfWork.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InjectExample.Component
{
/// <summary>
/// 业务单元操作接口
/// </summary>
public interface IUnitOfWork
{
#region 属性
/// <summary>
/// 获取 当前单元操作是否已被提交
/// </summary>
bool IsCommitted { get; }
#endregion
#region 方法
/// <summary>
/// 提交当前单元操作的结果
/// </summary>
/// <param name="validateOnSaveEnabled">保存时是否自动验证跟踪实体</param>
/// <returns></returns>
int Commit(bool validateOnSaveEnabled = true);
/// <summary>
/// 把当前单元操作回滚成未提交状态
/// </summary>
void Rollback();
#endregion
}
}
IUnitOfWorkContext.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace InjectExample.Component
{
/// <summary>
/// 数据单元操作接口
/// </summary>
public interface IUnitOfWorkContext : IUnitOfWork, IDisposable
{
/// <summary>
/// 注册一个新的对象到仓储上下文中
/// </summary>
/// <typeparam name="TEntity"> 要注册的类型 </typeparam>
/// <param name="entity"> 要注册的对象 </param>
void RegisterNew<TEntity>(TEntity entity) where TEntity : class;// EntityBase<TKey>;
/// <summary>
/// 批量注册多个新的对象到仓储上下文中
/// </summary>
/// <typeparam name="TEntity"> 要注册的类型 </typeparam>
/// <param name="entities"> 要注册的对象集合 </param>
void RegisterNew<TEntity>(IEnumerable<TEntity> entities) where TEntity : class;// EntityBase<TKey>;
/// <summary>
/// 注册一个更改的对象到仓储上下文中
/// </summary>
/// <typeparam name="TEntity"> 要注册的类型 </typeparam>
/// <param name="entity"> 要注册的对象 </param>
void RegisterModified<TEntity>(TEntity entity) where TEntity : class;// EntityBase<TKey>;
/// <summary>
/// 使用指定的属性表达式指定注册更改的对象到仓储上下文中
/// </summary>
/// <typeparam name="TEntity">要注册的类型</typeparam>
/// <param name="propertyExpression">属性表达式,包含要更新的实体属性</param>
/// <param name="entity">附带新值的实体信息,必须包含主键</param>
void RegisterModified<TEntity>(Expression<Func<TEntity, object>> propertyExpression, TEntity entity)
where TEntity : class;// EntityBase<TKey>;
/// <summary>
/// 注册一个删除的对象到仓储上下文中
/// </summary>
/// <typeparam name="TEntity"> 要注册的类型 </typeparam>
/// <param name="entity"> 要注册的对象 </param>
void RegisterDeleted<TEntity>(TEntity entity) where TEntity : class;// EntityBase<TKey>;
/// <summary>
/// 批量注册多个删除的对象到仓储上下文中
/// </summary>
/// <typeparam name="TEntity"> 要注册的类型 </typeparam>
/// <param name="entities"> 要注册的对象集合 </param>
void RegisterDeleted<TEntity>(IEnumerable<TEntity> entities) where TEntity : class;// EntityBase<TKey>;
DataSet SqlQueryForDataSet(CommandType commandType, string sql, SqlParameter[] parameters);
}
}
MefInjectionProvider.cs
/* ==============================================================================
* 功能描述:MefInjectionProvider
* 创 建 者:蒲奎民
* 创建日期:2016-08-29 10:17:02
* CLR Version :4.0.30319.42000
* 包引用 :
* Microsoft.Composition.1.0.30
* ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Composition;
using System.Composition.Convention;
using System.Composition.Hosting;
using System.Composition.Hosting.Core;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace InjectExample.Component
{
/// <summary>
/// 手动调用的注入类,调用方法:MefInjectionProvider.GetExport<T>(); 一般在静态类里面用,非静态类中可以用自动注入方式使用
/// </summary>
public static class MefInjectionProvider
{
private static CompositionHost _container;
private static ExportFactory<CompositionContext> _exportFactory;
static MefInjectionProvider()
{
InitInjection();
}
public static Export<CompositionContext> CreateContext()
{
return _exportFactory.CreateExport();
}
public static void Test()
{
}
public static T GetExport<T>()
{
return (T)CreateContext().Value.GetExport(typeof(T));
}
internal static void InitInjection()
{
var conventions = new ConventionBuilder();
var configuration = new ContainerConfiguration()
.WithDefaultConventions(conventions)
.WithAssemblies(Configuration.DefaultAssemblies());
_container = configuration.CreateContainer();
var factoryContract = new CompositionContract(
typeof(ExportFactory<CompositionContext>),
null,
new Dictionary<string, object> { { "SharingBoundaryNames", new[] { Boundaries.HttpRequest, Boundaries.DataConsistency, Boundaries.UserIdentity } } }
);
_exportFactory = (ExportFactory<CompositionContext>)_container.GetExport(factoryContract);
}
public static void Dispose()
{
if (_container != null)
{
_container.Dispose();
}
}
}
public static class Configuration
{
/// <summary>
/// 或取应用程序 Bin 路径
/// </summary>
/// <param name="inWeb">是否 web 应用</param>
/// <returns></returns>
public static IList<Assembly> DefaultAssemblies()
{
var exts = new[] { "exe", "dll" };
var dir = HttpRuntime.BinDirectory != null ? 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());
return configuration;
}
}
public static class Boundaries
{
/// <summary>
/// The boundary within which a current HTTP request is accessible.
/// </summary>
public const string HttpRequest = "HttpRequest";
/// <summary>
/// The boundary within which a consistent view of persistent data is available.
/// </summary>
public const string DataConsistency = "DataConsistency";
/// <summary>
/// The boundary within which a single user can be identified.
/// </summary>
public const string UserIdentity = "UserIdentity";
}
}
UnitOfWorkContextBase.cs
/* ==============================================================================
* 功能描述:UnitOfWorkContextBase
* 创 建 者:蒲奎民
* 创建日期:2016-08-29 10:59:20
* CLR Version :4.0.30319.42000
* ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace InjectExample.Component
{
/// <summary>
/// 单元操作实现基类
/// </summary>
public abstract class UnitOfWorkContextBase : IUnitOfWorkContext
{
/// <summary>
/// 获取 当前使用的数据访问上下文对象
/// </summary>
protected abstract DbContext Context { get; }
/// <summary>
/// 获取 当前单元操作是否已被提交
/// </summary>
public bool IsCommitted { get; private set; }
public DbContext DbContext { get { return Context; } }
/// <summary>
/// 提交当前单元操作的结果
/// </summary>
/// <param name="validateOnSaveEnabled">保存时是否自动验证跟踪实体</param>
/// <returns></returns>
public int Commit(bool validateOnSaveEnabled = false)
{
//validateOnSaveEnabled=true时莫名其妙报错,暂时去掉
validateOnSaveEnabled = false;
if (IsCommitted)
{
return 0;
}
try
{
int result = Context.SaveChanges(validateOnSaveEnabled);
IsCommitted = true;
return result;
}
catch (DbEntityValidationException ex)
{
var entry = ex.EntityValidationErrors.First().Entry;
var err = ex.EntityValidationErrors.First().ValidationErrors.First();
var msg = err.ErrorMessage;
try
{
var displayName = entry.Entity.GetType().GetProperty(err.PropertyName).GetPropertyDisplayName();
msg = string.Format(msg, displayName, entry.CurrentValues.GetValue<object>(err.PropertyName));
}
catch (Exception)
{
}
throw new Exception(msg);
}
catch (DbUpdateException e)
{
if (e.InnerException != null && e.InnerException.InnerException is SqlException)
{
var sqlEx = e.InnerException.InnerException as SqlException;
//string msg = DataHelper.GetSqlExceptionMessage(sqlEx.Number);
throw sqlEx; //PublicHelper.ThrowDataAccessException("提交数据更新时发生异常:" + msg, sqlEx);
}
throw;
}
}
/// <summary>
/// 把当前单元操作标记成未提交状态,发生错误时,不能回滚事务,不要调用!
/// </summary>
public void Rollback()
{
IsCommitted = false;
}
public void Dispose()
{
//if (!IsCommitted)
//{
// Commit();
//}
//if (Context != null)
// Context.Dispose();
}
/// <summary>
/// 为指定的类型返回 System.Data.Entity.DbSet,这将允许对上下文中的给定实体执行 CRUD 操作。
/// </summary>
/// <typeparam name="TEntity"> 应为其返回一个集的实体类型。 </typeparam>
/// <returns> 给定实体类型的 System.Data.Entity.DbSet 实例。 </returns>
public DbSet<TEntity> Set<TEntity>() where TEntity : class// EntityBase<TKey>
{
return Context.Set<TEntity>();
}
/// <summary>
/// 注册一个新的对象到仓储上下文中
/// </summary>
/// <typeparam name="TEntity"> 要注册的类型 </typeparam>
/// <param name="entity"> 要注册的对象 </param>
public void RegisterNew<TEntity>(TEntity entity) where TEntity : class// EntityBase<TKey>
{
EntityState state = Context.Entry(entity).State;
if (state == EntityState.Detached)
{
Context.Entry(entity).State = EntityState.Added;
}
IsCommitted = false;
}
/// <summary>
/// 批量注册多个新的对象到仓储上下文中
/// </summary>
/// <typeparam name="TEntity"> 要注册的类型 </typeparam>
/// <param name="entities"> 要注册的对象集合 </param>
public void RegisterNew<TEntity>(IEnumerable<TEntity> entities) where TEntity : class// EntityBase<TKey>
{
try
{
Context.Configuration.AutoDetectChangesEnabled = false;
foreach (TEntity entity in entities)
{
RegisterNew<TEntity>(entity);
}
}
finally
{
Context.Configuration.AutoDetectChangesEnabled = true;
}
}
public void RegisterModified<TEntity>(IEnumerable<TEntity> entities) where TEntity : class// EntityBase<TKey>
{
try
{
Context.Configuration.AutoDetectChangesEnabled = false;
foreach (TEntity entity in entities)
{
EntityState state = Context.Entry(entity).State;
if (state == EntityState.Detached)
{
Context.Entry(entity).State = EntityState.Modified;
}
IsCommitted = false;
}
}
finally
{
Context.Configuration.AutoDetectChangesEnabled = true;
}
}
/// <summary>
/// 注册一个更改的对象到仓储上下文中
/// </summary>
/// <typeparam name="TEntity"> 要注册的类型 </typeparam>
/// <param name="entity"> 要注册的对象 </param>
public void RegisterModified<TEntity>(TEntity entity) where TEntity : class// EntityBase<TKey>
{
Context.Update<TEntity>(entity);
IsCommitted = false;
}
/// <summary>
/// 使用指定的属性表达式指定注册更改的对象到仓储上下文中
/// </summary>
/// <typeparam name="TEntity">要注册的类型</typeparam>
/// <param name="propertyExpression">属性表达式,包含要更新的实体属性</param>
/// <param name="entity">附带新值的实体信息,必须包含主键</param>
public void RegisterModified<TEntity>(Expression<Func<TEntity, object>> propertyExpression, TEntity entity) where TEntity : class// EntityBase<TKey>
{
//Context.Update<TEntity>(propertyExpression, entity);
//IsCommitted = false;
}
/// <summary>
/// 注册一个删除的对象到仓储上下文中
/// </summary>
/// <typeparam name="TEntity"> 要注册的类型 </typeparam>
/// <param name="entity"> 要注册的对象 </param>
public void RegisterDeleted<TEntity>(TEntity entity) where TEntity : class// EntityBase<TKey>
{
Context.Entry(entity).State = EntityState.Deleted;
IsCommitted = false;
}
/// <summary>
/// 批量注册多个删除的对象到仓储上下文中
/// </summary>
/// <typeparam name="TEntity"> 要注册的类型 </typeparam>
/// <param name="entities"> 要注册的对象集合 </param>
public void RegisterDeleted<TEntity>(IEnumerable<TEntity> entities) where TEntity : class// EntityBase<TKey>
{
try
{
Context.Configuration.AutoDetectChangesEnabled = false;
foreach (TEntity entity in entities)
{
RegisterDeleted<TEntity>(entity);
}
}
finally
{
Context.Configuration.AutoDetectChangesEnabled = true;
}
}
/// <summary>
/// 从仓储上下文中删除注册的对象
/// </summary>
/// <typeparam name="TEntity"> 要删除注册的类型 </typeparam>
/// <param name="entity"> 要删除的对象 </param>
public void RegisterRemove<TEntity>(TEntity entity) where TEntity : class// EntityBase<TKey>
{
Context.Set<TEntity>().Remove(entity);
IsCommitted = false;
}
/// <summary>
/// EF SQL 语句返回 dataTable
/// </summary>
/// <param name="context"></param>
/// <param name="sql"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public DataSet SqlQueryForDataSet(CommandType commandType, string sql, SqlParameter[] parameters)
{
using (var conn = new SqlConnection { ConnectionString = Context.Database.Connection.ConnectionString })
{
if (conn.State != ConnectionState.Open)
{
conn.Open();
}
var cmd = new SqlCommand { Connection = conn, CommandText = sql, CommandType = commandType, CommandTimeout = conn.ConnectionTimeout };
if (parameters != null && parameters.Length > 0)
{
foreach (var item in parameters)
{
cmd.Parameters.Add(item);
}
}
var adapter = new SqlDataAdapter(cmd);
var ds = new DataSet();
adapter.Fill(ds);
cmd.Parameters.Clear();
return ds;
}
}
}
}
至此,Component层也完成了,里面一些其他公共方法 ,我就不一一写出了,到时候我会在文章最后附上源码。
3、Core 数据访问层处理
有了Component层之后,Core层对于开发人员来说就变得十分简单了,但是要创建几个简单的类去满足Component层的需要的映射对象。
SysUserConfiguration.cs
using InjectExample.Component;
using InjectExample.Model;
/* ==============================================================================
* 功能描述:SysUserConfiguration
* 创 建 者:蒲奎民
* 创建日期:2016-08-29 14:33:08
* CLR Version :4.0.30319.42000
* ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Composition;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InjectExample.Core.Configurations
{
/// <summary>
/// 实体类-数据表映射 此代码应由模版自动生成
/// EfDbContext 会把实现 IEntityMapper 接口的实体类自动添加到数据库实体上下文,如果没有查询数据库时会报表
/// </summary>
[Export(typeof(IEntityMapper))]
public partial class SysUserConfiguration : EntityTypeConfiguration<SysUser>, IEntityMapper
{
/// <summary>
/// 实体类-数据表映射构造函数
/// </summary>
public SysUserConfiguration()
{
SysUserConfigurationAppend();
}
/// <summary>
/// 额外的数据映射
/// </summary>
partial void SysUserConfigurationAppend();
/// <summary>
/// 将当前实体映射对象注册到当前数据访问上下文实体映射配置注册器中
/// </summary>
/// <param name="configurations">实体映射配置注册器</param>
public void RegistTo(ConfigurationRegistrar configurations)
{
configurations.Add(this);
}
}
}
SysUserInfoConfiguration.cs
using InjectExample.Component;
using InjectExample.Model;
/* ==============================================================================
* 功能描述:SysUserConfiguration
* 创 建 者:蒲奎民
* 创建日期:2016-08-29 14:33:08
* CLR Version :4.0.30319.42000
* ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Composition;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InjectExample.Core.Configurations
{
/// <summary>
/// 实体类-数据表映射 此代码应由模版自动生成
/// EfDbContext 会把实现 IEntityMapper 接口的实体类自动添加到数据库实体上下文,如果没有查询数据库时会报表
/// </summary>
[Export(typeof(IEntityMapper))]
public partial class SysUserInfoConfiguration : EntityTypeConfiguration<SysUserInfo>, IEntityMapper
{
/// <summary>
/// 实体类-数据表映射构造函数
/// </summary>
public SysUserInfoConfiguration()
{
SysUserInfoConfigurationAppend();
}
/// <summary>
/// 额外的数据映射
/// </summary>
partial void SysUserInfoConfigurationAppend();
/// <summary>
/// 将当前实体映射对象注册到当前数据访问上下文实体映射配置注册器中
/// </summary>
/// <param name="configurations">实体映射配置注册器</param>
public void RegistTo(ConfigurationRegistrar configurations)
{
configurations.Add(this);
}
}
}
ISysUserRepository.cs
using InjectExample.Component;
using InjectExample.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InjectExample.Core.Repositories
{
/// <summary>
/// 仓储操作层接口 此代码应由模版自动生成
/// </summary>
public partial interface ISysUserRepository : IRepository<SysUser>
{ }
}
ISysUserInfoRepository.cs
using InjectExample.Component;
using InjectExample.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InjectExample.Core.Repositories
{
/// <summary>
/// 仓储操作层接口 此代码应由模版自动生成
/// </summary>
public partial interface ISysUserInfoRepository : IRepository<SysUserInfo>
{ }
}
SysUserRepository.cs
using InjectExample.Component;
using InjectExample.Model;
/* ==============================================================================
* 功能描述:ChannelSubInfoRepository
* 创 建 者:蒲奎民
* 创建日期:2016-08-29 11:21:51
* 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;
namespace InjectExample.Core.Repositories.Impl
{
/// <summary>
/// 仓储操作层实现 此代码应由模版自动生成
/// </summary>
[Shared(Boundaries.DataConsistency)]
[Export(typeof(ISysUserRepository))]
internal partial class SysUserRepository : EfRepositoryBase<SysUser>, ISysUserRepository
{ }
}
SysUserInfoRepository.cs
using InjectExample.Component;
using InjectExample.Model;
/* ==============================================================================
* 功能描述:ChannelSubInfoRepository
* 创 建 者:蒲奎民
* 创建日期:2016-08-29 11:21:51
* 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;
namespace InjectExample.Core.Repositories.Impl
{
/// <summary>
/// 仓储操作层实现 此代码应由模版自动生成
/// </summary>
[Shared(Boundaries.DataConsistency)]
[Export(typeof(ISysUserInfoRepository))]
internal partial class SysUserInfoRepository : EfRepositoryBase<SysUserInfo>, ISysUserInfoRepository
{ }
}
下面是真正的数据操作Service类,随便怎么写,这里演示两表连接查询方法
ISysUserService.cs
using InjectExample.Model;
using InjectExample.Model.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InjectExample.Core.SysUsers
{
public interface ISysUserService
{
SysUserVModel GetById(long id);
}
}
SysUserService.cs,Service方法实现,引用Repository时,使用Import属性自动注入
using InjectExample.Core.Repositories;
using InjectExample.Model;
using InjectExample.Model.ViewModel;
/* ==============================================================================
* 功能描述:SysUserService
* 创 建 者:蒲奎民
* 创建日期:2016-08-29 11:26:42
* 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;
namespace InjectExample.Core.SysUsers.Impl
{
[Export(typeof(ISysUserService))]
public class SysUserService : CoreServiceBase, ISysUserService
{
[Import]
public ISysUserRepository SysUserRepository { get; set; }
[Import]
public ISysUserInfoRepository SysUserInfoRepository { get; set; }
public SysUserVModel GetById(long id)
{
var user = (from u in SysUserRepository.Entities
join ui in SysUserInfoRepository.Entities
on u.ID equals ui.SysUserId
select new SysUserVModel
{
ID = u.ID,
Age = u.Age,
Name = u.Name,
Remark = u.Remark,
LoginCount = ui.LoginCount,
LastLoginTime = ui.LastLoginTime
}).FirstOrDefault();
return user;
}
}
}
这里,Core层也完成了。
4、BLL逻辑处理层实现
BLL实现上,这里就是简单引用一下Service层的方法。
ISysUserContract.cs
using InjectExample.Model;
using InjectExample.Model.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InjectExample.BLL.SysUsers
{
public interface ISysUserContract
{
SysUserVModel GetById(long id);
}
}
SysUserContract.cs,引用 Service层时,也是使用 Import 属性自动注入
using InjectExample.Component;
using InjectExample.Core.SysUsers;
using InjectExample.Model;
using InjectExample.Model.ViewModel;
/* ==============================================================================
* 功能描述:SysUserContract
* 创 建 者:蒲奎民
* 创建日期:2016-08-29 11:32:59
* 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;
namespace InjectExample.BLL.SysUsers.Impl
{
[Export(typeof(ISysUserContract))]
public class SysUserContract : ISysUserContract
{
[Import]
public ISysUserService SysUserService { get; set; }
public SysUserVModel GetById(long id)
{
if (id <= 0) throw new BusinessException("ID不正确!");
return SysUserService.GetById(id);
}
}
}
BLL代码完成。
5、web表示层处理
因为依赖注入框架,内部用的是反射机制,所以在web运行后,首先要做的就是初始化加载dll,还有一些其他的内容。
在Global.asax 的 Application_Start 方法中,调用方法初始化数据
protected void Application_Start()
{
//禁用MVC响应标头
MvcHandler.DisableMvcResponseHeader = true;
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
//MefInjectionProvider.Test();
//Db初始化
DatabaseInitializer.Initialize();
DatabaseInitializer.PreCreateView();
}
还有,依赖注入的初始化,继承于 IHttpModule ,所以在webconfig文件中加入注册代码,也能实现自动初始化,web.config加内容:
<modules>
<add name="CompositionScopeModule" type="InjectExample.Component.Mvc.CompositionScopeModule" preCondition="managedHandler" />
<add name="RemoveHttpHeadModule" type="InjectExample.Component.Mvc.RemoveHttpHeadModule" />
</modules>
在App_Start下加FilterConfig.cs文件,内容:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace InjectExample.Web.App_Start
{
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
}
web.config加连接字符串:
<connectionStrings>
<add name="InjextExampleEntities" connectionString="Data Source=192.168.9.222;Initial Catalog=Example;Persist Security Info=True;User ID=dev;Password=123456;MultipleActiveResultSets=True;App=EntityFramework;" providerName="System.Data.SqlClient" />
</connectionStrings>
然后,就可以运行了,再添加测试数据显示控制器和View页面:
HomeController.cs
using InjectExample.BLL.SysUsers;
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace InjectExample.Web.Controllers
{
public class HomeController : Controller
{
[Import]
public ISysUserContract SysUserContract { get; set; }
//
// GET: /Home/
public ActionResult Index()
{
ViewBag.SysUser = SysUserContract.GetById(1);
return View();
}
}
}
Index.cshtml
@{
Layout = null;
var model = ViewBag.SysUser as InjectExample.Model.ViewModel.SysUserVModel;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>这是测试首页</title>
</head>
<body>
<div>
这是测试首页<br />
用户名称 @(model.Name)<br />
用户年龄 @(model.Age)<br />
登陆次数 @(model.LoginCount)<br />
最后登陆 @(model.LastLoginTime)<br />
</div>
</body>
</html>
这次可以显示测试数据了,附运行结果图:
框架搭建完毕,此框架的使用,也是很方便,一般Model层、Core层的Configurations和Repositories中的代码,都可用CodeSmith等模版生成器自动生成,开发人员只需要实现具体的数据访问和逻辑处理层就可以在web中调用了,非常方便。
这个框架搭建起来,比上一单的简单三层框架要复杂了很多,不过只要理解了,只是耗时多一点而已。
源码免费下地地址:
http://download.csdn.net/detail/pukuimin1226/9616604
版权声明:
作者:真爱无限
出处:http://blog.csdn.net/pukuimin1226/
本文为博主原创文章版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接.