一、通过AutoFac实现IOC

1、使用Nuget引入Autofac包,由于自己是MVC项目,所以引入了三个

2、创建Autofac的配置

/// <summary>
        ///  负责调用autofac框架实现业务逻辑层和数据仓储层程序集中的类型对象的创建
        ///  负责创建MVC控制器类的对象(调用控制器中的有参构造函数),接管DefaultControllerFactory的工作
        /// </summary>
        public static void Register()
        {
            #region 逐个注册方法
            ////创建autofac管理注册类的容器实例
            //var builder = new ContainerBuilder();
            ////下面就需要为这个容器注册它可以管理的类型
            ////builder的Register方法可以通过多种方式注册类型,之前在控制台程序里面也演示了好几种方式了。
            //builder.RegisterType<People>().As<IPeople>();

            ////builder.RegisterType<DefaultController>().InstancePerDependency();
            ////使用Autofac提供的RegisterControllers扩展方法来对程序集中所有的Controller一次性的完成注册
            //builder.RegisterControllers(Assembly.GetExecutingAssembly());
            ////生成具体的实例
            //var container = builder.Build();
            ////下面就是使用MVC的扩展 更改了MVC中的注入方式.
            //DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
            #endregion

            #region 批量注册方法

            //实例化一个autofac的创建容器
            var builder = new ContainerBuilder();

            #region 一种注册方式
            //告诉autofac框架,将来要创建的控制器类存放在哪个程序集(CommonFrame.Web)
            Assembly controllerAss = Assembly.Load("CommonFrame.Web");
            builder.RegisterControllers(controllerAss).PropertiesAutowired();//支持构造方式和属性方式方式注入,不加PropertiesAutowired则只支持构造方式

            //告诉autofac框架注册Service层所在程序集中的所有类的对象实例到IService层所在程序集中的所有类的对象实例
            Assembly iserAss = Assembly.Load("CommonFrame.IService");
            Assembly serAss = Assembly.Load("CommonFrame.Service");
            //创建serAss中的所有类的instance以此类的实现接口存储
            builder.RegisterAssemblyTypes(iserAss, serAss).AsImplementedInterfaces().PropertiesAutowired();//支持构造方式注入和属性方式注入,不加PropertiesAutowired则只支持构造方式

            //告诉autofac框架注册Repository层所在程序集中的所有类的对象实例到IRepository层所在程序集中的所有类的对象实例
            Assembly irepAss = Assembly.Load("CommonFrame.IRepository");
            Assembly repAss = Assembly.Load("CommonFrame.Repository");
            //创建serAss中的所有类的instance以此类的实现接口存储
            builder.RegisterAssemblyTypes(irepAss, repAss).AsImplementedInterfaces().PropertiesAutowired();//支持构造方式注入和属性方式注入,不加PropertiesAutowired则只支持构造方式
            #endregion

            #region 另一种注册方式
            ////如需加载实现的程序集,将dll拷贝到bin目录下即可,不用引用dll
            //var iServices = Assembly.Load("IocAufoFac.IServices");
            //var services = Assembly.Load("IocAufoFac.Services");
            //var iRepository = Assembly.Load("IocAufoFac.IRepository");
            //var repository = Assembly.Load("IocAufoFac.Repository");

            ////根据名称约定(服务层的接口和实现均以Services结尾),实现服务接口和服务实现的依赖
            //builder.RegisterAssemblyTypes(iServices, services)
            //  .Where(t => t.Name.EndsWith("Services"))
            //  .AsImplementedInterfaces();

            ////根据名称约定(数据访问层的接口和实现均以Repository结尾),实现数据访问接口和数据访问实现的依赖
            //builder.RegisterAssemblyTypes(iRepository, repository)
            //  .Where(t => t.Name.EndsWith("Repository"))
            //  .AsImplementedInterfaces();
            #endregion

            //创建一个Autofac的容器
            var container = builder.Build();
            //将MVC的控制器对象实例 交由autofac来创建
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
            #endregion
        }
View Code

3、在Global.asax的Application_Start启动方法中添加方法调用

//autofac注入
AutoFacConfig.Register();

4、在使用时通过属性注入得到

/// <summary>
/// 属性注入
/// </summary>
public IService_Common_Members Iservice_common_members { get; set; }

 

二、通过PostSharp实现AOP

1、使用Nuget引入PostSharp包

2、新增一个Aop_PostSharp类,代码中存在与记录日志的混合,请将就着看,代码如下

using log4net;
using PostSharp.Aspects;
using PostSharp.Extensibility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace CommonFrame.Web
{
    [Serializable]
    public class Aop_PostSharp : OnMethodBoundaryAspect
    {
        private static readonly log4net.ILog _logger;

        private string _methodName;
        
        private int _hashCode;

        static Aop_PostSharp()
        {
            if (!PostSharpEnvironment.IsPostSharpRunning)
            {
                _logger =
                    log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
            }
        }

        
        public Aop_PostSharp()
        {
            // Do nothing
        }

        public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
        {
            _methodName = "【类名:"+method.DeclaringType.Name + "/方法名:" + method.Name+"";
        }
        
        public override void RuntimeInitialize(MethodBase method)
        {
            _hashCode = this.GetHashCode();
        }

        /// <summary>
        /// 方法执行前
        /// </summary>
        /// <param name="args"></param>
        public override void OnEntry(MethodExecutionArgs args)
        {
            //_logger.InfoFormat(">>> Entry [{0}] {1}", _hashCode, _methodName);
        }

        /// <summary>
        /// 正常结束时
        /// </summary>
        /// <param name="args"></param>
        public override void OnSuccess(MethodExecutionArgs args)
        {
            //_logger.InfoFormat(">>> Success [{0}] {1}", _hashCode, _methodName);
        }

        /// <summary>
        /// 方法执行后
        /// </summary>
        /// <param name="args"></param>
        public override void OnExit(MethodExecutionArgs args)
        {
            //_logger.InfoFormat("<<< Exit [{0}] {1}", _hashCode, _methodName);
        }

        /// <summary>
        /// 抛出异常时,记录程序无法处理的异常信息
        /// 已经try catch处理过的,不进入该流程
        /// </summary>
        /// <param name="args"></param>
        public override void OnException(MethodExecutionArgs args)
        {
            string expMsg = string.Format("!!! Exception [{0}] {1} {2}", _hashCode, _methodName, args.Exception.ToString());
            _logger.ErrorFormat(expMsg, args.Exception);
            //重定向到指定的错误页面
            string errorurl = "/Error/Index";
            System.Web.HttpContext.Current.Response.Redirect(errorurl);
        }
    }
}
View Code

3、使用方法,在需要进行aop切入的action上加入上面的类名,具体参照下图

 

三、通过log4net实现日志记录

 1、使用Nuget引入log4net包

 2、在Web.config同一目录下添加Log4Net.config,配置如下

<?xml version="1.0" encoding="UTF-8"?>
<log4net debug="false">
  <!--Error-->
  <appender name="ErrorLog" type="log4net.Appender.RollingFileAppender">
    <!--是否续写-->
    <param name="AppendToFile" value="true" />
    <!--最小锁定模型以允许多个进程可以写入同一个文件-->
    <param name="LockingModel" value="log4net.Appender.FileAppender.MinimalLock" />
    <param name="StaticLogFileName" value="true" />
    <!--保存路径-->
    <param name="File" value="log\\" />
    <param name="DatePattern" value="yyyyMMdd_Error.LOG" />
    <param name="StaticLogFileName" value="false" />
    <param name="RollingStyle" value="Date" />
    <filter type="log4net.Filter.LevelRangeFilter">
      <levelMin value="ERROR" />
      <levelMax value="ERROR" />
    </filter>
    <layout type="log4net.Layout.PatternLayout">
      <param name="ConversionPattern" value="时间:%d %n级别:%level %n类名:%c%n文件:%F 第%L行%n错误日志:%m%n-----------------------------------------%n%n" />
    </layout>
  </appender>
  <!--Error-->

  <!--Info-->
  <appender name="InfoLog" type="log4net.Appender.RollingFileAppender">
    <!--是否续写-->
    <param name="AppendToFile" value="true" />
    <!--最小锁定模型以允许多个进程可以写入同一个文件-->
    <param name="LockingModel" value="log4net.Appender.FileAppender.MinimalLock" />
    <param name="StaticLogFileName" value="true" />
    <!--保存路径-->
    <param name="File" value="log\\" />
    <param name="DatePattern" value="yyyyMMdd_In\fo.LOG" />
    <param name="StaticLogFileName" value="false" />
    <param name="RollingStyle" value="Date" />
    <filter type="log4net.Filter.LevelRangeFilter">
      <levelMin value="INFO" />
      <levelMax value="INFO" />
    </filter>
    <layout type="log4net.Layout.PatternLayout">
      <param name="ConversionPattern" value="时间:%d %n级别:%level %n类名:%c%n文件:%F 第%L行%n日志内容:%m%n-----------------------------------------%n%n" />
    </layout>
  </appender>

  <!--按日志容量分割日志文件 10KB一个-->
  <appender name="LogFileAppenderBySize" type="log4net.Appender.RollingFileAppender" >
    <!--是否续写-->
    <param name="AppendToFile" value="true" />
    <!--最小锁定模型以允许多个进程可以写入同一个文件-->
    <param name="LockingModel" value="log4net.Appender.FileAppender.MinimalLock" />

    <param name="StaticLogFileName" value="true" />

    <!--按照文件的大小进行变换日志文件-->
    <param name="RollingStyle" value="Size" />
    <param name="File" value="log.txt" />
    <!--单个文件最大数量 好像只有在 按Size分割时有效-->
    <param name="MaximumFileSize" value="200KB"/>
    <!--保留的log文件数量 超过此数量后 自动删除之前的   好像只有在 按Size分割时有效-->
    <param name="MaxSizeRollBackups" value="2" />

    <param name="StaticLogFileName" value="false" />
    <layout type="log4net.Layout.PatternLayout">
      <param name="ConversionPattern" value="发生时间:%d %n事件级别:%level %n程序文件:%F 第%L行%n日志内容:%m%n-----------------------------------------%n%n" />
    </layout>
  </appender>

  <!--记录日志到数据库-->
  <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
    <bufferSize value="1" />
    <!--缓冲大小-->
    <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    <connectionString value="Data Source=.;Initial Catalog=Common;User ID=sa;Password=123" />
    <commandText value="INSERT INTO Common_Log([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)" />
    <parameter>
      <parameterName value="@log_date" />
      <dbType value="DateTime" />
      <layout type="log4net.Layout.RawTimeStampLayout" />
    </parameter>
    <parameter>
      <parameterName value="@thread" />
      <dbType value="String" />
      <size value="255" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%thread" />
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@log_level" />
      <dbType value="String" />
      <size value="50" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%level" />
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@logger" />
      <dbType value="String" />
      <size value="255" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%logger" />
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@message" />
      <dbType value="String" />
      <size value="4000" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%message" />
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@exception" />
      <dbType value="String" />
      <size value="2000" />
      <layout type="log4net.Layout.ExceptionLayout" />
    </parameter>
  </appender>

  <root>
    <level value="INFO" />
    <!--启用日志级别为ERROR的日志记录-->
    <appender-ref ref="ErrorLog" />
    <!--启用日志级别为INFO的日志记录-->
    <appender-ref ref="InfoLog" />
    <!--启用按容量分割-->
    <!--<appender-ref ref="LogFileAppenderBySize" />-->
    <!--启用保存到数据库-->
    <appender-ref ref="AdoNetAppender" />
  </root>

</log4net>
View Code
USE [Common]
GO

/****** Object:  Table [dbo].[Common_Log]    Script Date: 06/04/2018 14:29:58 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[Common_Log](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Date] [datetime] NOT NULL,
    [Thread] [varchar](255) NOT NULL,
    [Level] [varchar](50) NOT NULL,
    [Logger] [varchar](255) NOT NULL,
    [Message] [varchar](4000) NOT NULL,
    [Exception] [varchar](2000) NULL,
 CONSTRAINT [PK_Log] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO
LOG表CREATE语句

3、具体使用方法,本项目都是与AOP进行配合,进行自动记录日志,使日志功能与其他业务功能基本完全解耦

 

现已上传将项目源代码上传至GitHub,以便需要的人参考,也望大神指点

GitHub地址:https://github.com/zhuanshujianghai/NET-MVC4-EF6