【问题标题】:Spring.NET, C#, Dependency Injection and transactionsSpring.NET、C#、依赖注入和事务
【发布时间】:2012-05-03 07:01:10
【问题描述】:

我目前正在使用 MVC3、Spring.NET 和 FluentNHibernate 开发一个 ASP.NET 项目。

我的背景主要是java,但是有一些Spring框架的暴露,所以Spring.NET应该很容易,peasy,对吧?

应用程序架构相当普通,控制器使用服务,服务使用 DAO,DAO 使用 NHibernate 实体和映射。

目前,我对当前的情况感到头疼:

我的一个服务方法使用了一个 Dao,它是使用 spring.net 注入的。当我使用 [Transaction] 属性注释服务方法时,DAO 依赖项停止注入到我的服务中,导致 NullReferenceExceptions。

任何关于为什么会发生这种情况的想法将不胜感激。

一些代码来说明问题。这不是实际的代码,但非常接近。首先是抽象测试类,它扩展了spring类 AbstractTransactionalSpringContextTests :

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Namespace.Dao;
using Namespace.Entities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Spring.Testing.Microsoft;

namespace Namespace.Tests.Services
{

  [TestClass]
  public class AbstractServiceTest : AbstractTransactionalSpringContextTests
  {
    public AbstractServiceTest()
    {
    }


    protected override string[] ConfigLocations
    {
      get { return new string[]
          {
             "assembly://Assembly/Namespace.Config/dao.xml",
             "assembly://Assembly/Namespace.Config/Services.xml",
             "assembly://Assembly/Namespace.Config/web.xml"
          }; 
      }
    }
  }
}

这个类被扩展来创建实际的测试类:

using System.Collections.Generic;
using Namespace.Entities;
using Namespace.Services.Assets;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Namespace.Tests.Services
{
  [TestClass]
  public class AssetsServiceTest : AbstractServiceTest
  {

    public AssetsServiceTest()
    {
    }

    private AssetsService assetsService;

    public AssetsService AssetsService
    {
      get { return assetsService; }
      set { assetsService = value; }
    }


   [TestMethod]
   public void TestGetFacilities()
   {
     Assert.IsNotNull(assetsService);

     /* THIS ASSERTION FAILS when assetsService.GetFacilities has the [Transaction] attribute */ 
     Assert.IsNotNull(assetsService.FacilityDao);  

     IList<Facility> facilities = assetsService.GetFacilities();
     Assert.IsNotNull(facilities);
   }
  }
}

这里是服务类:

using System.Collections.Generic;
using Namespace.Dao;
using Namespace.Entities;
using Namespace.Models;
using Spring.Transaction;
using Spring.Transaction.Interceptor;
using Facility = Namespace.Entities.Facility;

namespace Namespace.Services.Assets
{
  public class AssetsService
  {
    public AssetsService()
    {
     System.Console.Out.WriteLine("AssetsService created");
    }

    private FacilityDao _facilityDao;

    public FacilityDao FacilityDao
    {
      get
      {
        return _facilityDao;
      }
      set
      {
        _facilityDao = value;
      }
    }

    [Transaction(TransactionPropagation.Required, ReadOnly = true)]
    public IList<Facility> GetFacilities()
    {
      return _facilityDao.FetchAll();
     }
  }
}

最后是浓缩和合并的 web.xml/applicationContext :

<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="http://www.springframework.net">

 <db:provider id="DbProvider"
     provider="System.Data.SqlClient"
     connectionString="Data Source=localhost;...;"/>

  <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate32">

    <property name="DbProvider" ref="DbProvider"/>
    <property name="SessionFactory" ref="SessionFactory"/>

</object>

 <tx:attribute-driven transaction-manager="transactionManager"/>

 <object type="Namespace.Dao.FacilityDao, Namespace" id="FacilityDao">
    <property name="SessionFactory" ref="SessionFactory"/>
 </object>

 <object type="Namespace.Services.Assets.AssetsService, Namespace" id="AssetsService">
   <property name="FacilityDao" ref="FacilityDao" />
 </object>

</objects>

编辑:

感谢 Max 和 Marijn 的提示,我现在已将 AssetsService 更改为使用接口而不是实际实现。但是,问题仍然存在。以下是修改后的细节。

namespace Namespace.Services.Assets
{
  public class AssetsService
  {
    public AssetsService()
    {
      System.Console.Out.WriteLine("AssetsService created");
    }

    public IFacilityDao FacilityDao { get; set; }

    [Transaction(TransactionPropagation.Required, ReadOnly = true)]
    public IList<Facility> GetFacilities()
     {
      return FacilityDao.FetchAll();
    }

  }
}

IFacilityDao:

namespace Namespace.Dao
{
    public interface IFacilityDao : IDao<Facility>
    {}

    public class FacilityDao : DaoBase<Facility>, IFacilityDao
    {

    }
}

IDao:

namespace Namespace.Dao
{
  public interface IDao<T>
  {
    T Fetch(int id);
    T FindByName(string name);
    IList<T> FetchAll();
    void SaveOrUpdate(T entity);
    void Delete(T entity);
  }
}

DaoBase:

namespace Namespace.Dao
{
    public abstract class DaoBase<T> : IDao<T>
    {
        public ISessionFactory SessionFactory { get; set; }

        public T Fetch(int id)
        {
            var result = SessionFactory
                      .GetCurrentSession()
                      .CreateCriteria(typeof(T))
                      .Add(Restrictions.Eq("ID", id))
                      .UniqueResult<T>();

            return result;
        }

     //.... 
}

}

【问题讨论】:

    标签: c# dependency-injection transactions spring.net


    【解决方案1】:

    注意:见下方更新

    您的AssetService.FacilityDao 成员应该是接口类型而不是具体的FacilityDao 类型;否则 spring.net 将无法创建用于拦截的事务代理。尝试抽象一个IFacilityDao 接口并将AssetService 更改为:

    public class AssetsService
    {
      public IFacilityDao FacilityDao { get; set; }
      // snip...
    }
    

    更新

    问题是你的AssetService.GetFacilities方法不能被代理;尝试使其成为虚拟(如果没有代理接口,spring 创建一个基于装饰器的代理,代理所有 public virtual 方法);如果失败,请尝试以我之前为 dao 建议的类似方式引入IAssetService 接口。并将其注入到您的测试的 IAssetService 属性中。

    对不起,我之前错过了。

    一些补充说明

    Assert.IsNotNull(assetsService.FacilityDao); 失败的原因是 AssetService 实例被注入到测试夹具 (autowired by type through spring testing infrastructure) 上。由于 transaction 属性,一个 aop 代理 被注入。这个代理的目标是注入了FacilityDao的“真实”对象。

    但是您断言的assetsServiceaop 代理。最初,您没有在 AssetService 类和 spring created a decorator proxy 上指定任何接口 - 一个继承自 AssetService 的代理,所有虚拟方法都被覆盖并委托给目标对象。

    断言返回 null,因为在这个代理上,设施 dao 永远不会被注入。

    在没有应用事务属性的情况下,断言不会返回 null,因为注入了“未代理”对象。

    【讨论】:

    • 查看我的更新;使GetFacilities 虚拟或创建IAssetService 接口。
    【解决方案2】:

    Marijin 是对的:如果你想使用 Spring.NET 的 AOP 能力(并且使用事务属性基本上是 AOP)你的 bean(用 Java 术语来说)需要实现一个接口。 原因是 Spring.NET 创建了一个代理对象,从技术上讲,它是在运行时创建的某个类的实例。抛出 NullReferenceException 是因为 .NET 无法将此代理类转换为您的具体类型。这只能通过使用接口来完成。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-01-31
      • 2011-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多