2012-05-03 21 views
1

私は現在、MVC3、Spring.NET、FluentNHibernateを使用してASP.NETプロジェクトの開発を開始しています。Spring.NET、C#、Dependency Injection and transactions

私の背景は主にJavaですが、いくつかのSpringフレームワークエクスポージャーがあるため、Spring.NETは簡単で、ピースでなければなりません。

アプリケーションアーキテクチャは、サービスを使用するコントローラ、NHibernateエンティティとマッピングを使用するDAOとDAOを使用するサービスではかなりバニラです。

瞬間、私は現在のシナリオで私の頭を悩まています:

私のサービスメソッドの一つがspring.netを用いて注入されダオを、使用しています。 [Transaction]属性でサービスメソッドに注釈を付けると、DAO依存関係が私のサービスに注入されなくなり、NullReferenceExceptionsが発生します。

なぜこのようなことが起こるかについてのご意見をお待ちしております。

問題を説明するコードです。これは実際のコードではありませんが、かなり近いです。第1ばねクラス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> 

EDIT: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; 
     } 

    //.... 
} 

}

FacilityDao is not proxied

答えて

2

注:AssetService.FacilityDao部材ではなくコンクリートFacilityDaoタイプのインタフェースタイプであるべき

下記アップデート参照。それ以外の場合、spring.netは傍受のためのトランザクションプロキシーを作成することができません。IFacilityDaoインターフェイスを抽象化してみて、何かにあなたのAssetServiceを変更します。

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

更新

問題は、あなたのAssetService.GetFacilitiesメソッドがプロキシすることができないことです。それを仮想化してみてください(春はデコレータベースプロキシを作成します)。公開仮想プロキシを使用するインタフェースがない場合は、メソッドをプロキシします。それが失敗した場合は、以前のDAOのために提案した同様の方法でIAssetServiceインターフェイスを導入してみてください。そして、あなたのテストのIAssetServiceプロパティにそれを注入します。

申し訳ありません。

失敗Assert.IsNotNull(assetsService.FacilityDao);AssetServiceインスタンスがテストフィクスチャ(autowired by type through spring testing infrastructure)に注入されるという事実から来ているいくつかの追加の説明

。トランザクション属性のために、aopプロキシが注入されます。このプロキシのターゲットは、FacilityDaoが挿入された「実際の」オブジェクトです。

あなたのアサーションのassetsServiceは、aopプロキシです。当初、AssetServiceクラスに指定されたインターフェイスと、AssetServiceを継承するプロキシであるすべての仮想メソッドがオーバーライドされ、ターゲットオブジェクトに委譲された、created a decorator proxyというバネが指定されていませんでした。

アサーションはnullを返します。このプロキシでは、施設daoは決して注入されないためです。

「非プロキシ」オブジェクトが挿入されたため、アサーションはトランザクション属性を適用しないとnullを戻しませんでした。

+0

問題が解決しない場合、私は詳細を追加しました。 –

+0

私の更新を参照してください。 'GetFacilities'を仮想化するか、' IAssetService'インタフェースを作成してください。 – Marijn

+0

IAssetServiceが解決しました。ありがとうございました! :) –

0

マリジンが正しいです:あなたがSpring.NETのAOP機能を使いたい場合(そしてトランザクション属性istは基本的にAOPを使用します)、あなたのBean(Java用語では)はインターフェイスを実装する必要があります。 その理由は、Spring.NETが技術的に実行時に作成されるいくつかのクラスのインスタンスであるプロキシオブジェクトを作成するからです。 .NETがこのプロキシクラスを具体的な型にキャストできないため、NullReferenceExceptionがスローされます。これは、インターフェイスを使用することによってのみ達成できます。

関連する問題