AOP Unity和EF POCO最佳工作流程

本文关键字:最佳 工作流程 POCO EF Unity AOP | 更新日期: 2023-09-27 18:05:49

当实体框架ORM从数据库构造POCO对象时,我想使用Unity容器在域POCO类上"注入"AOP方面

Unity可以通过两种方式轻松地将AOP方面"注入"到POCO类中(让我们分析一下利弊)

1)接口拦截器

你得到一个代理对象,它不是你的POCO类的派生对象。

你可以添加APO到一个先前存在的POCO对象,你不需要实例化一个新的。

2)虚拟方法拦截器

你得到一个对象,它是你的POCO类的派生对象。

你不能将AOP添加到先前存在的POCO对象中,你需要实例化一个新的。

而且. .我们可以。

a)配置统一容器并使用Resolve方法。

b)通过拦截类手动执行AOP注入,而不必配置Unity容器。

我还可以通过EF从数据库中检索POCO对象,然后使用带有接口拦截器的Intercept类将AOP注入到先前检索到的对象上。但是结果不会是POCO类的派生对象。

所以我需要在某些EF点(事件)上告诉EF它必须使用Unity容器来解析POCO类(或使用带有VirtualMethod Interceptor的Intercept类),因此AOP将被注入,对象将从POCO类派生。

BR Alex

AOP Unity和EF POCO最佳工作流程

我正在考虑使用PostSharp进行此类拦截

但如果我被迫使用Unity来做这件事,我能想到的唯一方法就是使用一个接口来解决问题和一个Decorator模式。

缺点是实现会很繁琐,不容易长期维护。

值得一提的是,你永远不应该访问没有在接口上声明或由装饰器指向的属性,因为对这些属性的更改不会反映

但是作为下面代码的练习

public interface IProduct
{
    int ProductID { get; set; }
    string ProductName { get; set; }
    decimal? UnitPrice { get; set; }
}
public class Product : IProduct
{
    public virtual int ProductID { get; set; }
    public virtual string ProductName { get; set; }
    public int? SupplierID { get; set; }
    public int? CategoryID { get; set; }
    public string QuantityPerUnit { get; set; }
    public virtual decimal? UnitPrice { get; set; }
    public short? UnitsInStock { get; set; }
    public short? UnitsOnOrder { get; set; }
    public short? ReorderLevel { get; set; }
    public bool Discontinued { get; set; }
}
public class AopProduct : Product
{
    private readonly IProduct _product;
    public override int ProductID { get { return _product.ProductID; } set { _product.ProductID = value; } }
    public override string ProductName { get { return _product.ProductName; } set { _product.ProductName = value; } }
    public override decimal? UnitPrice { get { return _product.UnitPrice; } set { _product.UnitPrice = value; } }
    public AopProduct(IProduct product)
    {
        _product = Intercept.ThroughProxy(product, new InterfaceInterceptor(), new IInterceptionBehavior[] { new LoggingInterceptionBehavior() });
    }
}
public class LoggingInterceptionBehavior : IInterceptionBehavior
{
    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        Console.WriteLine("Invoking {0} at {1}", input.MethodBase.Name, DateTime.Now.ToLongTimeString());
        return getNext()(input, getNext);
    }
    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }
    public bool WillExecute => true;
}
[TestClass]
public class UnitTest
{
    [TestMethod]
    public void AopProductShouldHaveAspectsAndBeProduct()
    {
        var product = new Product();
        var aopProduct = new AopProduct(product) { ProductID = 100, ProductName = "a product", UnitPrice = 12.5m };
        DisplayProduct(aopProduct);
        Assert.IsTrue(aopProduct is Product);
    }
    private static void DisplayProduct(Product product)
    {
        Console.WriteLine($"{product.ProductID} - {product.ProductName} - {product.UnitPrice}");
    }
}

如果我们运行代码显示

调用set_ProductID at 0:53:36

调用set_ProductID at 0:53:36

在0:53:36调用set_ProductName

在0:53:36调用set_ProductName

调用set_UnitPrice at 0:53:36

调用set_UnitPrice at 0:53:36

调用get_ProductID at 0:53:36

调用get_ProductID at 0:53:36

在0:53:36调用get_ProductName

在0:53:36调用get_ProductName

在0:53:36调用get_UnitPrice

在0:53:36调用get_UnitPrice

100 - a产品- 12,5

现在比较PostSharp的解决方案,你不需要任何额外的东西,没有接口,没有装饰,没有Unity,只是你的普通POCO类。我只是将一些属性"标记"为虚拟属性,只是为了将它们与那些我不想附加方面的属性区分开来,但如何应用这些方面取决于您。

public class Product
{
    public virtual int ProductID { get; set; }
    public virtual string ProductName { get; set; }
    public int? SupplierID { get; set; }
    public int? CategoryID { get; set; }
    public string QuantityPerUnit { get; set; }
    public virtual decimal? UnitPrice { get; set; }
    public short? UnitsInStock { get; set; }
    public short? UnitsOnOrder { get; set; }
    public short? ReorderLevel { get; set; }
    public bool Discontinued { get; set; }
}
[Serializable]
public sealed class LoggingOnMethodBoundaryAspect : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        Console.WriteLine("Invoking {0} at {1}", args.Method.Name, DateTime.Now.ToLongTimeString());
    }
}
[TestClass]
public class UnitTest
{

    [TestMethod]
    public void PSharpAopProductShouldHaveAspectsAndBeProduct()
    {
        var product = new Product() { ProductID = 100, ProductName = "a product", UnitPrice = 12.5m };
        DisplayProduct(product);
    }
    private static void DisplayProduct(Product product)
    {
        Console.WriteLine($"{product.ProductID} - {product.ProductName} - {product.UnitPrice}");
    }
}

和GlobalAspects.cs文件内容

using PostSharp.Extensibility;
[assembly: LoggingOnMethodBoundaryAspect(AttributeTargetTypes = "EFUnityAOPTest.Product", AttributeTargetTypeAttributes = MulticastAttributes.Public, AttributeTargetMemberAttributes = MulticastAttributes.Virtual)]

如果我们运行代码显示

调用set_ProductID at 1:10:39

调用set_ProductName在1:10:39

在1:10:39调用set_UnitPrice

调用get_ProductID at 1:10:39

在1:10:39调用get_ProductName

在1:10:39调用get_UnitPrice

100 - a产品- 12,5

快乐编码

BR Alex