0

У меня есть приложение ASP.NET MVC, которое использует Fluent NHibernate и AutoMapper. В основном я использую AutoMapper для сопоставления своих моделей с ViewModels и наоборот.Automapper with NHibernate - Как сохранить отображаемые объекты?

При выполнении последнего, сопоставления с моей модели просмотра назад к модели, мне интересно, как я могу сопоставить это с конкретным экземпляром из БД, поэтому, когда я передаю изменения обратно в БД (используя мой уровень репозитория 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 сессии, с тем чтобы обновить это объявление в БД, несмотря на объявление быть присвоен правильный ключ/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); 

или если вы используете NULLABLE целых чисел для ваших идентичностей проходят 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; 
} 

Got это от некоторых NHibernate гуру веб-сайт - хотя я не могу вспомнить, какой из них - это в основном треков и реконструирует сессию для вас в зависимости от контекста приложения, в котором вы находитесь, отлично работает для моего проекта.

Тогда вы просто вызываете стандартные методы менеджер:

ISession ctx = NHibernateSessionManager.Instance.GetSession(); 

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

Вы могли бы иметь большую обработку сессий уже реализованы - но из информации, что вы испытываете звуки как проблема сеанса так, дайте мне знать, если это помогает

Смежные вопросы