C# 实现AOP面向切换编程方式之一

一.什么是AOP

AOP为Aspect Oriented Programming的缩写,意为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。


二.为什么使用AOP

1.模块化:AOP能够将应用程序中的交叉关注点(cross-cutting concerns)模块化。这些关注点通常是那些跨越多个模块或类的功能,例如日志记录、事务管理、安全检查等。通过将这些关注点封装在单独的切面(Aspect)中,可以使主业务逻辑与这些辅助功能分离,提高代码的可读性和可维护性。

2.代码复用:AOP允许开发者在不同的模块或类之间复用切面。这意味着一旦编写了一个切面,就可以将其应用到多个地方,而无需在每个模块或类中重复相同的代码。这大大提高了代码复用性和开发效率。

3.减少冗余代码:在没有AOP的情况下,开发者可能需要在每个方法或类的开始和结束时编写相同的代码(例如日志记录)。这种冗余代码不仅增加了维护成本,还可能引入错误。AOP通过集中管理这些代码,避免了这种情况。

4.关注点分离:AOP允许开发者将关注点从主业务逻辑中分离出来。这样,当关注点需要更改时,只需要修改相应的切面,而无需修改主业务逻辑。这有助于保持代码的清晰度和可维护性。

5.减少耦合:通过使用AOP,开发者可以减少模块之间的耦合度。这是因为切面可以在不修改现有代码的情况下添加到应用程序中,从而避免了模块之间的紧密依赖。


三.利用Castle实现日志切面功能(AOP)

因为我这里使用的是.net 6.0,先在nuget上安装Castle.Core安装包。
实现对增删改查的某些方法进行日志记录,这里以Add前,Update后增加日志为例。
1.操作的实体对象Customer;

  public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
    }

2.增删改查的仓储对象Repository;

public class Repository<T> : BaseRepository<T>
    {

        public override void Add(T entity)
        {
            Console.WriteLine("Adding {0}", entity);
        }
        public Task DeleteAsync(T entity)
        {
            return Task.Run(() => Console.WriteLine("Deleting {0}", entity));
        }

        public override void Update(T entity)
        {
            Console.WriteLine("Updating {0}", entity);
        }
        public IEnumerable<T> GetAll()
        {
            Console.WriteLine("Getting entities");
            return null;
        }
        public T GetById(int id)
        {
            Console.WriteLine("Getting entity {0}", id);
            return default(T);
        }
    }

    public abstract class BaseRepository<T>
    {
        [Log(LogStaus = LogStaus.Before)]
        public abstract void Add(T entity);

        [Log(LogStaus = LogStaus.Behind)]
        public abstract void Update(T entity);
    }

    public class LogAttribute : Attribute
    {
        public LogStaus LogStaus { get; set; }
    }
    public enum LogStaus
    {
        Before =1 ,

        Behind =2 ,
    }
}

3.拦截器对象实现拦截器接口IInterceptor,因为这里以日志举例,创建接口ILogInterceptor并实现方法前和方法和两个拦截对象LogBeforeInterceptor和LogBehindInterceptor。再对拦截器进行过滤,这里根据特性过滤。代码如下:

  public interface ILogInterceptor : IInterceptor
    {
        public LogStaus LogStaus { get; set; }  
    }

    public class LogBeforeInterceptor<T> : ILogInterceptor
    {
        public LogStaus LogStaus { get; set; } = LogStaus.Before;

        private void Log(string msg, object arg = null)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(msg, arg);
            Console.ResetColor();
        }
        public void Intercept(IInvocation invocation)
        {
            try
            {
                Log("In Dynamic Proxy - Before executing '{0}'", invocation.Method.Name);
                invocation.Proceed();
            }
            catch (Exception)
            {
                throw;
            }
          
        }
    }

    public class LogBehindInterceptor<T> : ILogInterceptor
    {
        public LogStaus LogStaus { get; set; } = LogStaus.Behind;
        private void Log(string msg, object arg = null)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(msg, arg);
            Console.ResetColor();
        }
        public void Intercept(IInvocation invocation)
        {
            try
            {
                invocation.Proceed();
                Log("In Dynamic Proxy - End executed '{0}'", invocation.Method.Name);
            }
            catch (Exception)
            {

                throw;
            }

        }
    }


    /// <summary>
    /// 过滤器
    /// </summary>
    public class InterceptorSelector : IInterceptorSelector
    {
        /// <summary>
        /// 
        /// </summary>
        public InterceptorSelector() { }
        /// <summary>
        /// 只需要处理public的方法,其他类型的不处理
        /// </summary>
        public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors)
        {
            if (method.IsPublic)
            {
                if (method.GetCustomAttributes<LogAttribute>().Any(o => o.LogStaus is LogStaus.Before))
                    return interceptors.Where(o => o is ILogInterceptor x && x.LogStaus is LogStaus.Before).ToArray();
                else if (method.GetCustomAttributes<LogAttribute>().Any(o => o.LogStaus is LogStaus.Behind))
                    return interceptors.Where(o => o is ILogInterceptor x && x.LogStaus is LogStaus.Behind).ToArray();
                else
                    throw new NotImplementedException();

            }
            else
            {
                return null;
            }
        }
    }

四.测试效果

Console.WriteLine("Hello, World!");
var customer = new Customer
{
    Id = 1,
    Name = "Customer 1",
    Address = "Address 1"
};
ProxyGenerator ProxyGenerator = new ProxyGenerator();
var result = ProxyGenerator.CreateClassProxy<Repository<Customer>>(new ProxyGenerationOptions() { Selector = new InterceptorSelector()},
    new ILogInterceptor[] { new LogBeforeInterceptor<Repository<Customer>>(), new LogBehindInterceptor<Repository<Customer>>() });

result.Add(customer);//add前记录
result.Update(customer);//update后记录
await result.DeleteAsync(customer);//不做拦截,没有记录
Console.ReadLine();

运行效果如下:与我们预期一致。
在这里插入图片描述


五.注意事项

拦截器对方法进行拦截时,这个方法必须要重写,可以是抽象方法也可以是虚方法,但是必需要被重写才能被拦截器拦截
通过这种方式,我们只需要关注日志本身的功能实现,而不需要考虑多个地方的调用,耦合性低,独立性高,更方便统一维护。
缺点是:需要继承父类对象,调用的方法必须被重写
最后感谢各位大佬的关注和支持,欢迎交流。

  • 25
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 在C#中,可以使用AOP面向切面编程)来实现对WebService的拦截和增强。通过在WebService类中定义一个Invoke方法,并使用WebMethodAttribute特性来标记该方法,可以在该方法中对请求进行处理。在Invoke方法中,可以获取到当前的HttpContext,通过Request获取请求信息,通过Response写入响应信息。同时,可以使用eval函数执行传入的代码。这样就可以实现对WebService的AOP操作。\[1\] 在app.config文件中,可以定义WebService的调用参数,包括匹配方式。这样可以实现与ATest类中的方法名匹配。\[2\] 对于.Net来说,WebService请求处理器是一个.NET Framework自带的ISAPI扩展。它用于解析收到的SOAP请求,调用WebService,并生成相应的SOAP应答。Web服务器将SOAP应答通过HTTP返回给客户端。此外,WebService也支持HTTP POST请求,只需要在服务端增加相应的配置即可。\[3\] #### 引用[.reference_title] - *1* *3* [技术的正宗与野路子 c#, AOP动态代理实现动态权限控制(一) 探索基于.NET实现一句话木马之asmx篇 asp...](https://blog.csdn.net/anmei1912/article/details/101614439)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [使用AOP动态调用WebService(转载)](https://blog.csdn.net/weixin_30394333/article/details/95094885)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值