2012-05-03 4 views
1

В настоящее время я запускаю проект ASP.NET с использованием MVC3, Spring.NET и FluentNHibernate.Spring.NET, C#, Injection Dependency и транзакции

Мой фон - это в основном java, но с некоторой экспозицией Spring Framework, поэтому Spring.NET должен быть легким, peasy, правильно?

Архитектура приложения довольно ванильная, с контроллерами, использующими Службы, Службы, использующие DAO и DAO с использованием объектов и сопоставлений NHibernate.

На данный момент, я почесал голову в текущем сценарии:

Один из моих методов обслуживания использует Дао, который вводится с помощью Spring.NET. Когда я аннотирую метод службы с помощью атрибута [Транзакция], зависимость DAO перестает вставляться в мою службу, вызывая NullReferenceExceptions.

Любые идеи относительно того, почему это может произойти, будут очень признательны.

Некоторые коды, чтобы проиллюстрировать проблему. Это не настоящий код, но он довольно близок. Первый абстрактный класс теста, который расширяет весну класса 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:

Благодаря советы от Макса и 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... 
} 

Update

Проблемы заключается в том, что ваш метод AssetService.GetFacilities не может быть проксированной; попробуйте сделать его виртуальным (весна создает прокси-сервер на основе декоратора, который проксирует все public virtual методы, если нет интерфейсов к прокси); и если это не удается, попробуйте ввести интерфейс IAssetService аналогичным образом, который я предложил ранее для dao. И добавьте его к свойству IAssetService вашего теста.

Извините, я пропустил это раньше.

Некоторые дополнительные объяснения

Неисправный Assert.IsNotNull(assetsService.FacilityDao); исходит из того, что экземпляр AssetService впрыскивается на испытательном стенде (autowired by type through spring testing infrastructure). Из-за атрибута транзакции вводится aop proxy. Цель этого прокси - это «реальный» объект, в который в него вносится FacilityDao.

Но assetsService вашего утверждения aop proxy. Первоначально у вас не было никаких интерфейсов, указанных в вашем классе AssetService, а весна created a decorator proxy - прокси-сервер, который наследует от AssetService, при этом все виртуальные методы переопределяются и делегируются целевому объекту.

Утверждение возвращает null, потому что на этом прокси-сервере средство dao никогда не вводится.

Утверждение не вернуло нуль без примененного атрибута транзакции, поскольку был введен "непрощенный" объект.

+0

Я добавил дополнительную информацию, поскольку проблема сохраняется. –

+0

См. Мое обновление; сделать 'GetFacilities' виртуальным или создать интерфейс IAssetService. – Marijn

+0

IAssetService решил это. Спасибо! :) –

0

Marijin прав: если вы хотите использовать возможности Spring.NET для AOP (и используя атрибуты транзакций ist в основном AOP), ваши компоненты (в Java) должны реализовать интерфейс. Причина этого в том, что Spring.NET создает прокси-объект, который является технически экземпляром некоторого класса, который создается во время выполнения. Исключение NullReferenceException выбрано потому, что .NET не может отнести этот класс прокси к вашему конкретному типу. Это может быть достигнуто только с помощью интерфейсов.