0

我有一個使用Fluent NHibernate和AutoMapper的ASP.NET MVC應用程序。 我主要使用AutoMapper將我的模型映射到ViewModel,反之亦然。與NHibernate自動映射器 - 如何持久化映射的對象?

當做後者,從我的viewmodel映射回模型,我想知道如何將它映射到數據庫中的特定實例,所以當我將更改提交回數據庫(使用我的NHibernate存儲庫層,通過我的服務層),所做的更改仍然存在。

例子:

var advert = Mapper.Map<AdvertViewModel, Advert>(model); 
_advertService.UpdateAdvert(advert); // then calls repo which commits current NHibernate trans * Nothing in the DB changes * 

如果我試圖去犯我NHibernate的會議,以更新此廣告在DB儘管廣告被分配正確的密鑰/ ID作爲映射的一部分,我想因爲NHibernate會話對這個廣告實例(?)一無所知,它不會寫出更改。

因此,我想知道如何處理與NHibernate結合的映射場景?

回答

3

你可以做到以下幾點:

// fetch the domain model to update 
var domainModelToUpdate = _advertService.Get(viewModel.Id); 

// Map the properties that are present in the view model to the domain model 
// leaving other properties intact 
Mapper.Map<AdvertViewModel, Advert>(viewModel, domainModelToUpdate); 

// Update the domain model 
_advertService.UpdateAdvert(domainModelToUpdate); 

但是,如果視圖模型已經包含了一切,你不需要在更新之前獲取的域模型。所有你需要做的就是指定你的身份列映射unsaved-value,以使NHibernate的知道一個實例是瞬時的還是不是,然後使用SaveOrUpdate

Id(x => x.ID).WithUnsavedValue(0); 

,或者如果您使用的是可空的整數您的身份通過null

+0

viewmodel已經包含了所有東西,所以我試過你的後一種解決方案。我的Fluent NH Advert映射現在看起來像Id(x => x.Id).GeneratedBy.Identity()。UnsavedValue(0);並且我調用'SaveOrUpdate',但是然後得到錯誤'具有相同標識符值的不同對象已經與會話相關聯:'。任何想法,我可能會出錯? – marcusstarnes

+0

也許在你的會話和交易管理中。 –

+0

錯誤'具有相同標識符值的不同對象已與該會話相關聯,通常會在嘗試將實體保存在其生成的不同會話中時發生。作爲解決方法,您可以嘗試Session.Merge,但對於我來說 - 您應該改善會話生命週期 –

1

如果它確實是一個會話的問題 - 以下單可能會幫助你

/// <summary> 
/// Handles creation and management of sessions and transactions. It is a singleton because 
/// building the initial session factory is very expensive. Inspiration for this class came 
/// from Chapter 8 of Hibernate in Action by Bauer and King. Although it is a sealed singleton 
/// you can use TypeMock (http://www.typemock.com) for more flexible testing. 
/// </summary> 
public sealed class NHibernateSessionManager 
{ 
    #region Thread-safe, lazy Singleton 

    System.IO.StreamWriter ConsoleWriter = null; 

    /// <summary> 
    /// This is a thread-safe, lazy singleton. See http://www.yoda.arachsys.com/csharp/singleton.html 
    /// for more details about its implementation. 
    /// </summary> 
    public static NHibernateSessionManager Instance 
    { 
     get 
     { 
      return Nested.NHibernateSessionManager; 
     } 
    } 

    /// <summary> 
    /// Initializes the NHibernate session factory upon instantiation. 
    /// </summary> 
    private NHibernateSessionManager() 
    { 
     InitSessionFactory(); 
    } 

    /// <summary> 
    /// Assists with ensuring thread-safe, lazy singleton 
    /// </summary> 
    private class Nested 
    { 
     static Nested() { } 
     internal static readonly NHibernateSessionManager NHibernateSessionManager = 
      new NHibernateSessionManager(); 
    } 

    #endregion 

    private void InitSessionFactory() 
    { 
     // Hold the config var 
     FluentConfiguration config = Fluently.Configure(); 

     // Set the DB config 
     MsSqlConfiguration dbConfig = MsSqlConfiguration.MsSql2005.ConnectionString(ConfigurationManager.ConnectionStrings["iSearchConnection"].ConnectionString); 
     config.Database(dbConfig); 

     // Load mappings from this assembly 
     config.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())); 

     // Create session factory 
     sessionFactory = config.BuildSessionFactory(); 
    } 

    /// <summary> 
    /// Allows you to register an interceptor on a new session. This may not be called if there is already 
    /// an open session attached to the HttpContext. If you have an interceptor to be used, modify 
    /// the HttpModule to call this before calling BeginTransaction(). 
    /// </summary> 
    public void RegisterInterceptor(IInterceptor interceptor) 
    { 
     ISession session = ContextSession; 

     if (session != null && session.IsOpen) 
     { 
      throw new CacheException("You cannot register an interceptor once a session has already been opened"); 
     } 

     GetSession(interceptor); 
    } 

    public ISession GetSession() 
    { 
     return GetSession(null); 
    } 

    /// <summary> 
    /// Gets a session with or without an interceptor. This method is not called directly; instead, 
    /// it gets invoked from other public methods. 
    /// </summary> 
    private ISession GetSession(IInterceptor interceptor) 
    { 
     ISession session = ContextSession; 

     if (session == null) 
     { 
      if (interceptor != null) 
      { 
       session = sessionFactory.OpenSession(interceptor); 
      } 
      else 
      { 
       session = sessionFactory.OpenSession(); 
      } 

      ContextSession = session; 
     } 

     return session; 
    } 

    /// <summary> 
    /// Flushes anything left in the session and closes the connection. 
    /// </summary> 
    public void CloseSession() 
    { 
     ISession session = ContextSession; 

     if (session != null && session.IsOpen) 
     { 
      session.Flush(); 
      session.Close(); 
     } 

     if (ConsoleWriter != null) 
     { 
      ConsoleWriter.Flush(); 
      ConsoleWriter.Close(); 
     } 

     ContextSession = null; 
    } 

    public void BeginTransaction() 
    { 
     ITransaction transaction = ContextTransaction; 

     if (transaction == null) 
     { 
      transaction = GetSession().BeginTransaction(); 
      ContextTransaction = transaction; 
     } 
    } 

    public void CommitTransaction() 
    { 
     ITransaction transaction = ContextTransaction; 

     try 
     { 
      if (HasOpenTransaction()) 
      { 
       transaction.Commit(); 
       ContextTransaction = null; 
      } 
     } 
     catch (HibernateException) 
     { 
      RollbackTransaction(); 
      throw; 
     } 
    } 

    public bool HasOpenTransaction() 
    { 
     ITransaction transaction = ContextTransaction; 

     return transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack; 
    } 

    public void RollbackTransaction() 
    { 
     ITransaction transaction = ContextTransaction; 

     try 
     { 
      if (HasOpenTransaction()) 
      { 
       transaction.Rollback(); 
      } 

      ContextTransaction = null; 
     } 
     finally 
     { 
      CloseSession(); 
     } 
    } 

    /// <summary> 
    /// If within a web context, this uses <see cref="HttpContext" /> instead of the WinForms 
    /// specific <see cref="CallContext" />. Discussion concerning this found at 
    /// http://forum.springframework.net/showthread.php?t=572. 
    /// </summary> 
    private ITransaction ContextTransaction 
    { 
     get 
     { 
      if (IsInWebContext()) 
      { 
       return (ITransaction)HttpContext.Current.Items[TRANSACTION_KEY]; 
      } 
      else 
      { 
       return (ITransaction)CallContext.GetData(TRANSACTION_KEY); 
      } 
     } 
     set 
     { 
      if (IsInWebContext()) 
      { 
       HttpContext.Current.Items[TRANSACTION_KEY] = value; 
      } 
      else 
      { 
       CallContext.SetData(TRANSACTION_KEY, value); 
      } 
     } 
    } 

    /// <summary> 
    /// If within a web context, this uses <see cref="HttpContext" /> instead of the WinForms 
    /// specific <see cref="CallContext" />. Discussion concerning this found at 
    /// http://forum.springframework.net/showthread.php?t=572. 
    /// </summary> 
    private ISession ContextSession 
    { 
     get 
     { 
      if (IsInWebContext()) 
      { 
       return (ISession)HttpContext.Current.Items[SESSION_KEY]; 
      } 
      else 
      { 
       return (ISession)CallContext.GetData(SESSION_KEY); 
      } 
     } 
     set 
     { 
      if (IsInWebContext()) 
      { 
       HttpContext.Current.Items[SESSION_KEY] = value; 
      } 
      else 
      { 
       CallContext.SetData(SESSION_KEY, value); 
      } 
     } 
    } 

    private bool IsInWebContext() 
    { 
     return HttpContext.Current != null; 
    } 

    private const string TRANSACTION_KEY = "CONTEXT_TRANSACTION"; 
    private const string SESSION_KEY = "CONTEXT_SESSION"; 
    private ISessionFactory sessionFactory; 
} 

得到這個了一些NHibernate的大師網站 - 雖然我不記得是哪一個 - 它基本上跟蹤並重建一個會話無論你在哪個應用環境中爲你工作 - 對我的項目都非常有用。

然後你只需撥打經理的標準方法:

ISession ctx = NHibernateSessionManager.Instance.GetSession(); 

try 
{ 
    ctx.BeginTransaction(); 
    ctx.Update(entity); 
    ctx.CommitTransaction(); 
} 

你可能有一個偉大的會話處理已經實現了 - 但是從信息,你正在經歷聽起來像一個會話的問題,所以讓我知道如果有幫助

相關問題