0

我正在尝试使用 NHibernate 持久性设置 NServiceBus Sagas,但我认为我的某些配置不正确,因为我在连接到 127.0.0.1:8080 的服务时遇到错误。我能够让我的 saga 处理命令消息,但几秒钟后,控制台窗口中出现下面的错误消息,然后再次触发相同的命令,导致 saga 中的处理程序再次被调用。只要我允许应用程序运行,这种情况就会反复发生。我可以告诉 NHibernate 正在连接到我的数据库,因为它为 saga 数据创建了一个表,但是该表中没有任何内容。

我认为持久化 saga 数据存在错误,我的猜测是它可能正在尝试使用默认的 RavenDb saga 持久性,但我不确定为什么会这样。

我收到的错误消息如下:

WARN NServiceBus.Unicast.Transport.Transactional.TransactionalTransport [(null)] <(null)> - 未能为 ID=3753b476-7501-4fd8-90d0-b10aee95a578\22314 System.Net.WebException 的消息引发“收到传输消息”事件:无法连接到远程服务器 ---> System.Net.Sockets.SocketException: 无法建立连接,因为目标机器在 System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress) 在 System.Net.Sockets.Socket.InternalConnect(EndPoint remoteEP) 在 System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult,Exception&异常)---内部异常堆栈跟踪结束---在 NServiceBus.Unicast.UnicastBus.HandleTransportMessage(IBuilder childBuilder, TransportMessage msg) 在 NServiceBus.Unicast.UnicastBus.TransportMessageReceived(Object sender, TransportMessageReceivedEventArgs e) 在 System.EventHandler`1 .Invoke(Object sender, TEventArgs e) 在 NServiceBus.Unicast.Transport.Transactional.TransactionalTransport.OnTransportMessageReceived(TransportMessage msg)OnTransportMessageReceived(TransportMessage 消息)OnTransportMessageReceived(TransportMessage 消息)

我尝试使用的 saga 示例是(这里没什么特别的,无论我是否真的在 Handle 方法中做某事,都会发生同样的事情):

public class ItemSaga : Saga<ItemData>, IAmStartedByMessages<CreateItemCommand>
{
    public void Handle(CreateItemCommand message)
    {

    }
}

public class ItemData : ISagaEntity
{
    public Guid Id { get; set; }
    public string Originator { get; set; }
    public string OriginalMessageId { get; set; }
}

我的端点配置如下所示:

public class EndpointConfig : IConfigureThisEndpoint, AsA_Publisher, IWantCustomInitialization
{


    public void Init()
    {

        var container = new UnityContainer();
        container.AddNewExtension<Domain.UnityExtension>();

        Configure.With()
            .UnityBuilder(container)
            .JsonSerializer()
            .Log4Net()
            .MsmqSubscriptionStorage()
            .MsmqTransport()
                .PurgeOnStartup(true)
            .UnicastBus()
                .ImpersonateSender(false)
            .DisableTimeoutManager()
            .NHibernateSagaPersister()
            .CreateBus()
            .Start(() => Configure.Instance.ForInstallationOn<NServiceBus.Installation.Environments.Windows>().Install()); 
    }
}

我的 app.config 看起来像这样:

<MessageForwardingInCaseOfFaultConfig ErrorQueue="error"/>
  <MsmqTransportConfig NumberOfWorkerThreads="1" MaxRetries="5"/>

  <NHibernateSagaPersisterConfig UpdateSchema="true">
    <NHibernateProperties>
      <add Key="connection.provider" Value="NHibernate.Connection.DriverConnectionProvider"/>
      <add Key="connection.driver_class" Value="NHibernate.Driver.Sql2008ClientDriver"/>
      <add Key="connection.connection_string" Value="Data Source=(localdb)\v11.0;Integrated Security=True;AttachDbFileName=|DataDirectory|\App_Data\EventStore.mdf"/>
      <add Key="dialect" Value="NHibernate.Dialect.MsSql2012Dialect"/>
    </NHibernateProperties>
  </NHibernateSagaPersisterConfig>

  <connectionStrings>
    <add name="EventStore" connectionString="Data Source=(localdb)\v11.0;Integrated Security=True;AttachDbFileName=|DataDirectory|\App_Data\EventStore.mdf"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
     <dependentAssembly>
        <assemblyIdentity name="NHibernate" publicKeyToken="aa95f207798dfdb4" />
        <bindingRedirect oldVersion="0.0.0.0-3.3.0.4000" newVersion="3.3.1.4000" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>

只是几个注意事项:

这是来自我用来测试此功能的示例应用程序。它使用附加到 localdb 的本地数据库文件,但是我使用 SQL Server 2012 的完整应用程序表现出相同的行为。我还必须为 NHibernate 添加一个dependenteAssembly 条目,因为 NServiceBus.NHibernate NuGet 包当前绑定到较旧的程序集版本(截至本文发布)。

正如你所看到的,我也使用 Unity 作为我的 IOC,但我也用一个使用 Ninject 的项目复制了这一点。我将 EventStore 用于我的域存储,效果很好。我有命令处理程序来处理命令并通过 EventStore 发布事件以由其他进程处理。但是,我尝试禁用所有那些只留下我的 Saga 作为命令处理程序的人,但我仍然遇到同样的错误。

有没有人知道我可能做错了什么?

4

1 回答 1

0

我找到了解决问题的方法。使用内置 NServiceBus 配置文件似乎是一个问题。我没有在主机的命令行参数中指定配置文件,因此默认情况下它正在加载 NServiceBus.Production 配置文件。默认情况下,生产配置文件使用 RavenDB 进行所有持久性。

查看 GitHub 上的 NServiceBus 源代码,Production Profile Handler 在 ProfileActivated 方法中包含以下内容:

Configure.Instance.RavenPersistence();

if (!Configure.Instance.Configurer.HasComponent<ISagaPersister>())
    Configure.Instance.RavenSagaPersister();

if (!Configure.Instance.Configurer.HasComponent<IManageMessageFailures>())
    Configure.Instance.MessageForwardingInCaseOfFault();

if (Config is AsA_Publisher && !Configure.Instance.Configurer.HasComponent<ISubscriptionStorage>())
    Configure.Instance.RavenSubscriptionStorage();

这里有几点需要注意:

  • 配置文件将始终在 Configure 实例上调用 RavenPersistence()。如果已经定义了其他持久性,我还没有深入研究该方法的内部工作原理,看看它是否真的会绕过配置 Raven,但它总是会运行这个方法。
  • 当我附加到 NServiceBus 源并通过此代码进行调试时,在第二行中 HasComponent 返回 false 导致 RavenSagaPersister 配置运行。即使我在端点配置中定义了 NHibernateSagaPerister,也会发生这种情况。

我不确定这种行为是我的设计、错误还是配置错误。但是我的解决方法是创建自己的个人资料。我不得不将 NHibernate 配置调用从我的端点配置移动到我的新配置文件,但是一旦我这样做了,我就能够使用 NHibernate 持久性而不会出错。

我的自定义配置文件如下所示(我从生产配置文件的日志处理程序中借用了日志处理程序):

public class MyProfile : IProfile
{

}

internal class MyProfileProfileHandler : IHandleProfile<MyProfile>, IWantTheEndpointConfig
{
    void IHandleProfile.ProfileActivated()
    {
        Configure.Instance.NHibernateUnitOfWork();
        Configure.Instance.NHibernateSagaPersister();
        Configure.Instance.DBSubcriptionStorage();
        Configure.Instance.UseNHibernateTimeoutPersister();
    }

    public IConfigureThisEndpoint Config { get; set; }
}

public class MyProfileLoggingHandler : IConfigureLoggingForProfile<MyProfile>
{
    void IConfigureLogging.Configure(IConfigureThisEndpoint specifier)
    {
        SetLoggingLibrary.Log4Net<RollingFileAppender>(null,
            a =>
            {
                a.CountDirection = 1;
                a.DatePattern = "yyyy-MM-dd";
                a.RollingStyle = RollingFileAppender.RollingMode.Composite;
                a.MaxFileSize = 1024 * 1024;
                a.MaxSizeRollBackups = 10;
                a.LockingModel = new FileAppender.MinimalLock();
                a.StaticLogFileName = true;
                a.File = "logfile";
                a.AppendToFile = true;
            });

        if (GetStdHandle(STD_OUTPUT_HANDLE) == IntPtr.Zero)
            return;

        SetLoggingLibrary.Log4Net<ColoredConsoleAppender>(null,
          a =>
          {
              LiteLoggingHandler.PrepareColors(a);
              a.Threshold = Level.Info;
          }
      );
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr GetStdHandle(int nStdHandle);
    const int STD_OUTPUT_HANDLE = -11;
}

最后一点,根据标准 NHibernate 实践,我还必须将我的 saga 数据对象的所有属性设置为虚拟。一旦系统实际使用 NHibernate,这一点就变得非常明显。

public class ItemData : ISagaEntity
{
    public virtual Guid Id { get; set; }
    public virtual string Originator { get; set; }
    public virtual string OriginalMessageId { get; set; }
}

这是一个很长的解释,但希望它能帮助其他人。如果有人对完成此操作的更好方法或使用配置文件的正确方法有建议,请告诉我!

于 2012-10-18T19:55:54.183 回答