【发布时间】:2013-04-06 20:06:23
【问题描述】:
在尝试修改子对象然后保存父对象时,我的 ASP.NET WebForms 应用程序中的 Fluent Nhibernate 遇到了一些严重问题。
我的解决方案目前由 2 个项目组成:
- 核心:所有实体和存储库类都位于其中的类库
- 网站:ASP.NET 4.5 WebForms 应用程序
这是我的 Employee 对象的简单映射:
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.DateCreated);
Map(x => x.Username);
Map(x => x.FirstName);
Map(x => x.LastName);
HasMany(x => x.TimeEntries).Inverse().Cascade.All().KeyColumn("Employee_id");
}
}
这是我对 TimeEntry 对象的映射:
public class TimeEntryMap : ClassMap<TimeEntry>
{
public TimeEntryMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.DateCreated);
Map(x => x.Date);
Map(x => x.Length);
References(x => x.Employee).Column("Employee_id").Not.Nullable();
}
}
如标题所述,我在我的网络应用程序中使用每个请求一个会话,在 Gobal.asax 中使用此代码:
public static ISessionFactory SessionFactory = Core.SessionFactoryManager.CreateSessionFactory();
public static ISession CurrentSession
{
get { return (ISession)HttpContext.Current.Items["current.session"]; }
set { HttpContext.Current.Items["current.session"] = value; }
}
protected Global()
{
BeginRequest += delegate
{
System.Diagnostics.Debug.WriteLine("New Session");
CurrentSession = SessionFactory.OpenSession();
};
EndRequest += delegate
{
if (CurrentSession != null)
CurrentSession.Dispose();
};
}
另外,这是我的 SessionFactoryManager 类:
public class SessionFactoryManager
{
public static ISession CurrentSession;
public static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("Website.Properties.Settings.WebSiteConnString")))
.Mappings(m => m
.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(false, true))
.BuildSessionFactory();
}
public static ISession GetSession()
{
return (ISession)HttpContext.Current.Items["current.session"];
}
}
这是我的存储库类之一,用于处理 Employee 的对象数据操作:
public class EmployeeRepository<T> : IRepository<T> where T : Employee
{
private readonly ISession _session;
public EmployeeRepository(ISession session)
{
_session = session;
}
public T GetById(int id)
{
T result = null;
using (ITransaction tx = _session.BeginTransaction())
{
result = _session.Get<T>(id);
tx.Commit();
}
return result;
}
public IList<T> GetAll()
{
IList<T> result = null;
using (ITransaction tx = _session.BeginTransaction())
{
result = _session.Query<T>().ToList();
tx.Commit();
}
return result;
}
public bool Save(T item)
{
var result = false;
using (ITransaction tx = _session.BeginTransaction())
{
_session.SaveOrUpdate(item);
tx.Commit();
result = true;
}
return result;
}
public bool Delete(T item)
{
var result = false;
using (ITransaction tx = _session.BeginTransaction())
{
_session.Delete(_session.Load(typeof (T), item.Id));
tx.Commit();
result = true;
}
return result;
}
public int Count()
{
var result = 0;
using (ITransaction tx = _session.BeginTransaction())
{
result = _session.Query<T>().Count();
tx.Commit();
}
return result;
}
}
现在,这是我的问题。当我尝试插入员工时,一切都很好。更新也是完美的……好吧,只要我不更新 Employee 的“TimeEntries”属性中引用的 TimeEntry 对象之一……
这里是引发异常的地方(在 Web 项目的 ASPX 文件中):
var emp = new Employee(1);
foreach (var timeEntry in emp.TimeEntries)
{
timeEntry.Length += 1;
}
emp.Save();
这是引发的异常:
[NonUniqueObjectException:具有相同标识符的不同对象 值已与会话相关联:1,实体: Core.Entities.Employee]
基本上,每当我尝试
- 加载员工并
- 修改一个已保存的
TimeEntry,我得到了那个异常。
仅供参考,我尝试将存储库中的SaveOrUpdate() 替换为Merge()。它做得很好,但是当使用 Merge() 创建对象时,我的对象永远不会设置它的 ID。
我还尝试在我的存储库的每个函数中创建和刷新ISession。这是没有意义的,因为当我尝试加载 Employee 的 TimeEntries 属性时,就会引发异常,说由于 ISession 已关闭,该对象无法延迟加载...
我迷路了,希望能得到一些帮助。也欢迎对我的存储库提出任何建议,因为我对此很陌生。
谢谢你们!
【问题讨论】:
-
先加载对象,再更新。你不会得到那个例外。
-
@DarthVader 我修改了我的员工存储库以反映您的建议:
public T GetById(int id) { T result = null; using (ITransaction tx = _session.BeginTransaction()) { //result = _session.Get<T>(id); result = _session.Load<T>(id); tx.Commit(); } return result; }不幸的是,发生了同样的错误。也许我误解了您的评论:您是否建议我手动加载 List属性中的每个对象? -
好吧,正如错误所说,您已经在会话中获得了具有相同 ID 值的不同对象。我会在请求开始时设置断点,并在
CurrentSession上进行监视,以确定设置“不同对象”的位置和时间。 -
@Snixtor 集合中的对象在我的问题的最后一个sn-p中被修改。我想了解为什么我不能修改其中一个子对象,然后保存父对象以传播并提交所有更改。我认为这就是
.Cascade.All()属性的用途。
标签: asp.net nhibernate fluent-nhibernate repository webforms