如何通过实现Orchard.Data.ISessionLocator编写自定义Orchard会话定位器?

时间:2015-12-23 14:24:31

标签: c# asp.net-mvc migration orchardcms

我需要在Orchard 1.9.0安装中运行的Orchard模块中迁移一些数据。这里的问题是数据存储在另一台服务器上的外部数据库中,Orchard DB中的不是。因此,当调用迁移类方法时,内部Orchard使用Orchard.Data.ISessionLocator接口来检索数据库连接。遗憾的是,覆盖这种行为是不可能的,但我有想法通过创建自定义会话定位器来挂钩会话定位器。

自定义会话定位器如下所示,基于现有的类Orchard.Data.SessionLocator

public class CustomSessionLocator : Orchard.Data.ISessionLocator, Orchard.Data.ITransactionManager, System.IDisposable
{
  // public
    public CustomSessionLocator(Orchard.Data.ISessionFactoryHolder aSessionFactoryHolder)
    {
      Logger = Orchard.Logging.NullLogger.Instance;
      IsolationLevel = System.Data.IsolationLevel.ReadCommitted;

      mSessionFactoryHolder = aSessionFactoryHolder;

      mSessions = new System.Collections.Generic.Dictionary<SessionScope, NHibernate.ISession>();

      if (mForeignDBConnection == null)
      {
        string lConnectionString = "data source=myServer;initial catalog=myDB;persist security info=True;user id=xxx;password=xxx;MultipleActiveResultSets=True;";

        mForeignDBConnection = new System.Data.SqlClient.SqlConnection(lConnectionString);
      }
    }

    public NHibernate.ISession For(System.Type aEntityType)
    {
      Logger.Debug("Acquiring session for {0}", aEntityType);
      Demand();

      return mSessions[CurrentSessionScope];
    }

    public void Demand()
    {
      EnsureSession(IsolationLevel);
    }

    public void RequireNew()
    {
      RequireNew(IsolationLevel);
    }

    public void RequireNew(System.Data.IsolationLevel aLevel)
    {
      DisposeSession();
      EnsureSession(aLevel);
    }

    public void Cancel()
    {
      NHibernate.ISession lSession;

      if (mSessions.TryGetValue(CurrentSessionScope, out lSession) && lSession != null && !lSession.Transaction.WasRolledBack && lSession.Transaction.IsActive)
      {
        Logger.Debug("Rolling back transaction");
        lSession.Transaction.Rollback();
      }
    }

    public void Dispose()
    {
      DisposeSession();
    }

    public enum SessionScope
    {
      OrchardDefault, 
      ForeignDB      
    }

    public Orchard.Logging.ILogger Logger { get; set; }
    public System.Data.IsolationLevel IsolationLevel { get; set; }
    public SessionScope CurrentSessionScope { private get; set; }

  // private
    private void DisposeSession()
    {
      NHibernate.ISession lSession;

      if (mSessions.TryGetValue(CurrentSessionScope, out lSession) && lSession != null)
      {
        try
        {
          if (!lSession.Transaction.WasRolledBack && lSession.Transaction.IsActive)
          {
            Logger.Debug("Committing transaction");
            lSession.Transaction.Commit();
          }
        }
        finally
        {
          Logger.Debug("Disposing session");
          var lConnection = lSession.Connection;
          lSession.Close();
          lSession.Dispose();
          lSession = null;

          mSessions[CurrentSessionScope] = null;
        }
      }
    }

    private void EnsureSession(System.Data.IsolationLevel aLevel)
    {
      NHibernate.ISession lSession;

      if (mSessions.TryGetValue(CurrentSessionScope, out lSession) && lSession != null)
        return;

      var lSessionFactory = mSessionFactoryHolder.GetSessionFactory();
      Logger.Debug("Opening NHibernate session");

      if (CurrentSessionScope == SessionScope.ForeignDB)
      {
        lSession = lSessionFactory.OpenSession(mForeignDBConnection);

        // open connection otherwise the following lSession.BeginTransaction() fails with exception
        if (mForeignDBConnection.State == System.Data.ConnectionState.Closed)
          mForeignDBConnection.Open();
      }
      else
        lSession = lSessionFactory.OpenSession();

      mSessions[CurrentSessionScope] = lSession;

      lSession.BeginTransaction(aLevel);
    }

    private readonly Orchard.Data.ISessionFactoryHolder mSessionFactoryHolder;
    private System.Collections.Generic.Dictionary<SessionScope, NHibernate.ISession> mSessions;
    private static System.Data.SqlClient.SqlConnection mForeignDBConnection;
}

然后我有一个如下所示的迁移数据解释器:

public class ForeignDataMigrationInterpreter : Orchard.Data.Migration.Interpreters.DefaultDataMigrationInterpreter
{
  public ForeignDataMigrationInterpreter(
    Orchard.Environment.Configuration.ShellSettings aShellSettings,
    Orchard.Data.ISessionLocator aSessionLocator,
    System.Collections.Generic.IEnumerable<Orchard.Data.Migration.Interpreters.ICommandInterpreter> aCommandInterpreters,
    Orchard.Data.ISessionFactoryHolder aSessionFactoryHolder,
    Orchard.Reports.Services.IReportsCoordinator aReportsCoordinator)
    : base(aShellSettings, aSessionLocator, aCommandInterpreters, aSessionFactoryHolder, aReportsCoordinator) 
  {
    mSessionLocator = aSessionLocator as CustomSessionLocator;
  }

  public override void Visit(Orchard.Data.Migration.Schema.CreateTableCommand aCommand)
  {
    #if LIVE
      if (IsForeignDBCommand(aCommand.Name, ""))
        mSessionLocator.CurrentSessionScope = CustomSessionLocator.SessionScope.ForeignDB;
      else
        mSessionLocator.CurrentSessionScope = CustomSessionLocator.SessionScope.OrchardDefault;
    #endif

    base.Visit(aCommand);
  }

  ...

  private bool IsForeignDBCommand(...)
  {
    return ...;
  }

  private CustomSessionLocator mSessionLocator;
}

如您所见,外国数据的基本程序是

  1. Start Orchard
  2. 调用
  3. Migration类方法,其中包含SchemaBuilder.CreateTable()
  4. ForeignDataMigrationInterpreter.Visit(CreateTableCommand)被称为
  5. 自定义会话定位器的
  6. CurrentSessionScope已更新为SessionScope.ForeignDB
  7. CreateTableCommand已传入base class
  8. CustomSessionLocator.For()被称为
  9. CustomSessionLocator.EnsureSession()
  10. 结尾
  11. 返回范围SessionScope.ForeignDB
  12. 会话X
  13. 基类会将CreateTableCommand列入会话X
  14. 的交易
  15. 快进一些不相关的额外步骤并且事务被提交但是它永远不会返回并且发生超时异常
  16. 我的问题是

    1. 甚至可以通过这种方式迁移外国数据吗?
    2. 为什么会发生超时?

0 个答案:

没有答案