我有一个使用Fluent NHibernate和AutoMapper的ASP.NET MVC应用程序。 我主要使用AutoMapper将我的模型映射到ViewModels,反之亦然。
当执行后者时,从我的viewmodel映射回模型,我想知道如何将它映射到来自DB的特定实例,所以当我将更改提交回DB时(使用我的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会话,以便在数据库中更新此广告,尽管广告被分配了正确的Key / Id作为映射的一部分,我想因为NHibernate会话对此广告实例一无所知(?)它不会写下这些变化。
因此,我想知道如何与NHibernate一起处理这个映射场景?
答案 0 :(得分: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
。
答案 1 :(得分: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();
}
您可能已经实施了一个很棒的会话处理 - 但是根据信息,您遇到的声音听起来像是一个会话问题所以请告诉我这是否有帮助