使用会话 nhibernate .Net MVC 更新 M:N 数据
updating M:N data using session nhibernate .Net MVC
我想更新 M:N 数据,但是当我这样做时我遇到了这个异常:
Initializing[DataAccess.Model.Product#6]-Illegally attempted to associate a proxy with two open Sessions
我认为这与我的会话代码有关,但无法弄清楚你能帮帮我吗?
Here is a code of update session
public void Update(T entity)
{
using (ITransaction transaction = Session.BeginTransaction())
{
Session.Update(entity);
transaction.Commit();
}
}
控制器中的方法是这样的
[HttpPost]
public ActionResult Add(int product)
{
Create();
Product productD = new ProductDao().GetById(product);
ProductsOfBag productsOfBag = new ProductsOfBag();
User user = new UserDao().GetByLogin(User.Identity.Name);
Bag bag = new BagDao().GetByUser(user);
bag.Price += productD.Price;
bag.PriceDph += productD.PriceDph;
bag.NumberOfItems++;
ProductsOfBagDao productsOfBagDao = new ProductsOfBagDao();
productsOfBag.IdBag = bag;
productsOfBag.IdProduct = productD;
productsOfBagDao.Create(productsOfBag);
IList<ProductsOfBag> products = productsOfBagDao.GetByBag(bag);
productD.Bags = products;
bag.Products = products;
BagDao bagDao = new BagDao();
bagDao.Update(bag);
return RedirectToAction("Index", "Home");
}
Product.hbm.xml
<class name="Product" table="Products" lazy="true">
<id name="Id" column="id_product">
<generator class="native" />
</id>
<property name="Name" column="name" />
<property name="PriceDph" column="priceDph" />
<property name="Price" column="price" />
<property name="ProductState" column="productState" />
<property name="Maker" column="maker" />
<property name="Description" column="description" />
<property name="ProductWaranty" column="productWaranty" />
<property name="Points" column="points" />
<many-to-one name="Category" column="id_category" foreign-key="id_category" />
<property name="ImageName" column="imageName" />
<bag name="Bags" lazy="true"
inverse="true" batch-size="25" cascade="all-delete-orphan">
<key column="id_product" />
<one-to-many class="ProductsOfBag" />
</bag>
</class>
Bag.hbm.xml
<class name="Bag" table="Bags" lazy="true">
<id name="Id" column="id_bag">
<generator class="native" />
</id>
<property name="Price" column="price" />
<property name="PriceDph" column="priceDph" />
<property name="NumberOfItems" column="numberOfItems" />
<many-to-one name="IdUser" column="id_User" foreign-key="id_User" />
<bag name="Products" lazy="true"
inverse="true" batch-size="25" cascade="all-delete-orphan">
<key column="id_bag" />
<one-to-many class="ProductsOfBag" />
</bag>
</class>
Product.cs
public class Product : IEntity
{
public virtual int Id { get; set; }
[Required(ErrorMessage = "Název produktu je vyžadován")]
public virtual string Name { get; set; }
private double _priceDph;
public virtual double PriceDph
{
get
{
_priceDph = Price + Price*0.21;
return _priceDph;
}
set { _priceDph = value; }
}
[Required(ErrorMessage = "Cena je vyžadována")]
[Range(0, 9000000, ErrorMessage = "Cena nemůže být záporná")]
public virtual int Price { get; set; }
public virtual string ProductState { get; set; }
[Required(ErrorMessage = "Výrobce je vyžadován")]
public virtual string Maker { get; set; }
[AllowHtml]
public virtual string Description { get; set; }
[Required(ErrorMessage = "Záruka je vyžadována")]
public virtual int ProductWaranty { get; set; }
[Required(ErrorMessage = "Počet bodů je vyžadován")]
private int _points;
public virtual int Points
{
get
{
_points = (int)PriceDph/10;
return _points;
}
set { _points = value; }
}
public virtual ProductCategory Category { get; set; }
public virtual string ImageName { get; set; }
public virtual IList<ProductsOfBag> Bags { get; set; }
}
Bag.cs
public class Bag :IEntity
{
public virtual int Id { get; set; }
public virtual double Price { get; set; }
public virtual double PriceDph { get; set; }
public virtual int NumberOfItems { get; set; }
public virtual User IdUser { get; set; }
public virtual IList<ProductsOfBag> Products { get; set; }
}
NHibernateHelper.cs
public class NHibernateHelper
{
private static ISessionFactory _factory;
public static ISession Session
{
get
{
if (_factory == null)
{
var cfg = new Configuration();
_factory =
cfg.Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Hibernate.cfg.xml"))
.BuildSessionFactory();
}
return _factory.OpenSession();
}
}
}
存在与相似集合与其不同运行时实例的映射相关的问题。最好通过代码讨论,所以这是一个实体
public class Bag :IEntity
{
...
public virtual IList<ProductsOfBag> Products { get; set; }
}
这是第二个:
public class Product : IEntity
{
...
public virtual IList<ProductsOfBag> Bags { get; set; }
}
两者似乎属于同一类型:IList<ProductsOfBag>
,但实际上它们是两个不同的实例。也就是说这段代码
Product productD = ...;
Bag bag = ...;
...
ProductsOfBagDao productsOfBagDao = new ProductsOfBagDao();
productsOfBag.IdBag = bag;
productsOfBag.IdProduct = productD;
productsOfBagDao.Create(productsOfBag);
没问题,但下一行是真正的问题,异常的来源
// wrong lines
IList<ProductsOfBag> products = productsOfBagDao.GetByBag(bag);
// this is illegal
productD.Bags = products;
bag.Products = products;
这是不允许的。 每个 对象都有其自己的集合。这些集合是 NHibernate ISession
(productD.Bags
和 bag.Products
)的一部分 - they/instances 是在加载期间创建的。
所以,我们应该能够跳过,删除这 3 行并调用:
BagDao bagDao = new BagDao();
// this will instruct NHibernate to use cascade
bag.Products.Add(productsOfBag);
bagDao.Update(bag);
如果所有 DAO 对象共享同一个 ISession 实例(见下文),它现在应该可以工作了
原始部分
此外,当我们将 NHibernate 与 Web 应用程序一起使用时,主要是 ASP.NET MVC
或 Web API
,我们应该考虑与整个 web 请求相关的工作单元.
为了支持这种方法,我们可以使用内置的 AOP 过滤器或委托处理程序。大家可以按照这个综合post:
NHibernate session management in ASP.NET Web API
大部分是:
会话管理操作过滤器
(显示简化的代码片段)
// the AOP filter, MVC built in feature
public class NhSessionManagementAttribute : ActionFilterAttribute
{
... // some init stuff
// here we start session
public override void OnActionExecuting(HttpActionContext actionContext)
{
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
session.BeginTransaction();
}
// here we close it
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var session = SessionFactory.GetCurrentSession();
var transaction = session.Transaction;
if (transaction != null && transaction.IsActive)
{
transaction.Commit();
}
session = CurrentSessionContext.Unbind(SessionFactory);
session.Close();
}
一些类似的东西:
- No session bound to the current context when redirecting to login page
- Why does Nhibernate share the session across multiple requests in my MVC application?
- NHibernate session is closed when refereshing page
万一这不是问题,会话已经通过整个 Web 请求打开,我们会得到异常
... Illegally attempted to associate a proxy with two open Sessions ...
以防万一,我们在两个(或更多)会话期间将一个对象保存在内存中。这很容易(意外地)通过 ASP.NET MVC 功能实现:
return RedirectTo...
我们可能有一个已加载实体的实例...加载到某些 POST 操作/网络请求中...被 passed/redirected 加载到某些其他操作中。但这将有效地创建新的 Web 请求。这将触发 AOP 过滤器...这将关闭第一个会话并为新请求打开全新的会话。
为避免这种情况,我们应该严格区分
的操作
- 写入和
- 阅读
他们不应该分享任何东西。首先应该执行所有更新、插入和删除 - 提交事务...关闭会话。只能使用 ID 进行重定向。稍后,我们应该在一个全新的会话中,只读 - 正确提交和清除的东西...
我想更新 M:N 数据,但是当我这样做时我遇到了这个异常:
Initializing[DataAccess.Model.Product#6]-Illegally attempted to associate a proxy with two open Sessions
我认为这与我的会话代码有关,但无法弄清楚你能帮帮我吗?
Here is a code of update session
public void Update(T entity)
{
using (ITransaction transaction = Session.BeginTransaction())
{
Session.Update(entity);
transaction.Commit();
}
}
控制器中的方法是这样的
[HttpPost]
public ActionResult Add(int product)
{
Create();
Product productD = new ProductDao().GetById(product);
ProductsOfBag productsOfBag = new ProductsOfBag();
User user = new UserDao().GetByLogin(User.Identity.Name);
Bag bag = new BagDao().GetByUser(user);
bag.Price += productD.Price;
bag.PriceDph += productD.PriceDph;
bag.NumberOfItems++;
ProductsOfBagDao productsOfBagDao = new ProductsOfBagDao();
productsOfBag.IdBag = bag;
productsOfBag.IdProduct = productD;
productsOfBagDao.Create(productsOfBag);
IList<ProductsOfBag> products = productsOfBagDao.GetByBag(bag);
productD.Bags = products;
bag.Products = products;
BagDao bagDao = new BagDao();
bagDao.Update(bag);
return RedirectToAction("Index", "Home");
}
Product.hbm.xml
<class name="Product" table="Products" lazy="true">
<id name="Id" column="id_product">
<generator class="native" />
</id>
<property name="Name" column="name" />
<property name="PriceDph" column="priceDph" />
<property name="Price" column="price" />
<property name="ProductState" column="productState" />
<property name="Maker" column="maker" />
<property name="Description" column="description" />
<property name="ProductWaranty" column="productWaranty" />
<property name="Points" column="points" />
<many-to-one name="Category" column="id_category" foreign-key="id_category" />
<property name="ImageName" column="imageName" />
<bag name="Bags" lazy="true"
inverse="true" batch-size="25" cascade="all-delete-orphan">
<key column="id_product" />
<one-to-many class="ProductsOfBag" />
</bag>
</class>
Bag.hbm.xml
<class name="Bag" table="Bags" lazy="true">
<id name="Id" column="id_bag">
<generator class="native" />
</id>
<property name="Price" column="price" />
<property name="PriceDph" column="priceDph" />
<property name="NumberOfItems" column="numberOfItems" />
<many-to-one name="IdUser" column="id_User" foreign-key="id_User" />
<bag name="Products" lazy="true"
inverse="true" batch-size="25" cascade="all-delete-orphan">
<key column="id_bag" />
<one-to-many class="ProductsOfBag" />
</bag>
</class>
Product.cs
public class Product : IEntity
{
public virtual int Id { get; set; }
[Required(ErrorMessage = "Název produktu je vyžadován")]
public virtual string Name { get; set; }
private double _priceDph;
public virtual double PriceDph
{
get
{
_priceDph = Price + Price*0.21;
return _priceDph;
}
set { _priceDph = value; }
}
[Required(ErrorMessage = "Cena je vyžadována")]
[Range(0, 9000000, ErrorMessage = "Cena nemůže být záporná")]
public virtual int Price { get; set; }
public virtual string ProductState { get; set; }
[Required(ErrorMessage = "Výrobce je vyžadován")]
public virtual string Maker { get; set; }
[AllowHtml]
public virtual string Description { get; set; }
[Required(ErrorMessage = "Záruka je vyžadována")]
public virtual int ProductWaranty { get; set; }
[Required(ErrorMessage = "Počet bodů je vyžadován")]
private int _points;
public virtual int Points
{
get
{
_points = (int)PriceDph/10;
return _points;
}
set { _points = value; }
}
public virtual ProductCategory Category { get; set; }
public virtual string ImageName { get; set; }
public virtual IList<ProductsOfBag> Bags { get; set; }
}
Bag.cs
public class Bag :IEntity
{
public virtual int Id { get; set; }
public virtual double Price { get; set; }
public virtual double PriceDph { get; set; }
public virtual int NumberOfItems { get; set; }
public virtual User IdUser { get; set; }
public virtual IList<ProductsOfBag> Products { get; set; }
}
NHibernateHelper.cs
public class NHibernateHelper
{
private static ISessionFactory _factory;
public static ISession Session
{
get
{
if (_factory == null)
{
var cfg = new Configuration();
_factory =
cfg.Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Hibernate.cfg.xml"))
.BuildSessionFactory();
}
return _factory.OpenSession();
}
}
}
存在与相似集合与其不同运行时实例的映射相关的问题。最好通过代码讨论,所以这是一个实体
public class Bag :IEntity
{
...
public virtual IList<ProductsOfBag> Products { get; set; }
}
这是第二个:
public class Product : IEntity
{
...
public virtual IList<ProductsOfBag> Bags { get; set; }
}
两者似乎属于同一类型:IList<ProductsOfBag>
,但实际上它们是两个不同的实例。也就是说这段代码
Product productD = ...;
Bag bag = ...;
...
ProductsOfBagDao productsOfBagDao = new ProductsOfBagDao();
productsOfBag.IdBag = bag;
productsOfBag.IdProduct = productD;
productsOfBagDao.Create(productsOfBag);
没问题,但下一行是真正的问题,异常的来源
// wrong lines
IList<ProductsOfBag> products = productsOfBagDao.GetByBag(bag);
// this is illegal
productD.Bags = products;
bag.Products = products;
这是不允许的。 每个 对象都有其自己的集合。这些集合是 NHibernate ISession
(productD.Bags
和 bag.Products
)的一部分 - they/instances 是在加载期间创建的。
所以,我们应该能够跳过,删除这 3 行并调用:
BagDao bagDao = new BagDao();
// this will instruct NHibernate to use cascade
bag.Products.Add(productsOfBag);
bagDao.Update(bag);
如果所有 DAO 对象共享同一个 ISession 实例(见下文),它现在应该可以工作了
原始部分
此外,当我们将 NHibernate 与 Web 应用程序一起使用时,主要是 ASP.NET MVC
或 Web API
,我们应该考虑与整个 web 请求相关的工作单元.
为了支持这种方法,我们可以使用内置的 AOP 过滤器或委托处理程序。大家可以按照这个综合post:
NHibernate session management in ASP.NET Web API
大部分是:
会话管理操作过滤器
(显示简化的代码片段)
// the AOP filter, MVC built in feature
public class NhSessionManagementAttribute : ActionFilterAttribute
{
... // some init stuff
// here we start session
public override void OnActionExecuting(HttpActionContext actionContext)
{
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
session.BeginTransaction();
}
// here we close it
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var session = SessionFactory.GetCurrentSession();
var transaction = session.Transaction;
if (transaction != null && transaction.IsActive)
{
transaction.Commit();
}
session = CurrentSessionContext.Unbind(SessionFactory);
session.Close();
}
一些类似的东西:
- No session bound to the current context when redirecting to login page
- Why does Nhibernate share the session across multiple requests in my MVC application?
- NHibernate session is closed when refereshing page
万一这不是问题,会话已经通过整个 Web 请求打开,我们会得到异常
... Illegally attempted to associate a proxy with two open Sessions ...
以防万一,我们在两个(或更多)会话期间将一个对象保存在内存中。这很容易(意外地)通过 ASP.NET MVC 功能实现:
return RedirectTo...
我们可能有一个已加载实体的实例...加载到某些 POST 操作/网络请求中...被 passed/redirected 加载到某些其他操作中。但这将有效地创建新的 Web 请求。这将触发 AOP 过滤器...这将关闭第一个会话并为新请求打开全新的会话。
为避免这种情况,我们应该严格区分
的操作- 写入和
- 阅读
他们不应该分享任何东西。首先应该执行所有更新、插入和删除 - 提交事务...关闭会话。只能使用 ID 进行重定向。稍后,我们应该在一个全新的会话中,只读 - 正确提交和清除的东西...