当使用 Fluent Nhibernate AutoMap 时,我的多对多(全部级联)不会插入/更新子项
这是我的场景(在阅读 Fluent 的第一个项目和自动映射页面之后)。我的设置几乎相同,只是我的一些约定与默认值不同,例如表名称和外键列名称。我已经为相关的多对多关系提供了手动覆盖;一侧是相反的,另一侧不是。由于某种原因,这仍然没有级联添加。我可以单独手动添加图像。我还可以成功地沿着层次结构上下读取整个架构。
我尝试省略手动的多对多映射覆盖;但我最终在选择中得到了一个时髦的表名称 ImageToPhotoSession
,这失败了。
有什么想法吗?
我应该提到我正在使用: 休眠3 Fluent 1.2(测试版)
配置:
private readonly static ISessionFactory factory =
Fluently.Configure()
.Database(MySQLConfiguration.Standard.ShowSql().ConnectionString(o => o.FromConnectionStringWithKey("Default")))
.Mappings(m =>
m.AutoMappings.Add(AutoMap.AssemblyOf<Data.Image>(new TestMappingConfig())
.Override<Data.PhotoSession>(map => map
.HasManyToMany<Data.Image>(o => o.Images)
.Cascade.All()
.Table("photo_session_image"))
.Override<Data.Image>(map => map
.HasManyToMany(x => x.PhotoSessions)
.Cascade.All()
.Inverse()
.Table("photo_session_image"))
.Conventions.Add(
Table.Is(x => Inflector.Net.Inflector.Underscore(x.EntityType.Name)),
DefaultLazy.Always(),
DefaultCascade.All(),
ForeignKey.EndsWith("Id")
)))
.BuildSessionFactory();
这是我的数据实体:
public class Image
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int SizeOnDisk { get; set; }
public virtual string Hash { get; set; }
public virtual int Width { get; set; }
public virtual int Height { get; set; }
public virtual string Description { get; set; }
public virtual string Caption { get; set; }
public virtual DateTime CreatedUtc { get; set; }
public virtual DateTime ModifiedUtc { get; set; }
public virtual DateTime ProcessedUtc { get; set; }
public virtual string ContentType { get; set; }
public virtual Site Site { get; set; }
public virtual IList<PhotoSession> PhotoSessions { get; private set; }
public Image()
{
this.PhotoSessions = new List<PhotoSession>();
}
}
public class PhotoSession
{
public virtual int Id { get; private set; }
public virtual string Description { get; set; }
public virtual DateTime CreatedUtc { get; set; }
public virtual DateTime ModifiedUtc { get; set; }
public virtual DateTime? EventDate { get; set; }
public virtual IList<Image> Images { get; private set; }
public virtual Site Site { get; set; }
public PhotoSession()
{
Images = new List<Image>();
}
public virtual void AddImage(Image data)
{
data.PhotoSessions.Add(this);
this.Images.Add(data);
}
}
这是我尝试使用的代码片段。
var repo = new Repository();
var site = repo.All<Data.Site>().Where(o => o.Id == 2).FirstOrDefault();
var img = new Data.Image()
{
Name = "test",
Caption = "test",
ContentType = "image/jpeg",
CreatedUtc = DateTime.UtcNow,
Description = "test",
Hash = "test",
Height = 23,
ModifiedUtc = DateTime.UtcNow,
SizeOnDisk = 23,
Width = 23
};
site.AddImage(img);
//one to many, actually calls session.SaveOrUpdate(object) - fails to insert img
repo.Update(site);
//gets a sample session
var session = repo.All<Data.PhotoSession>().Where(o => o.Id == 25).FirstOrDefault();
session.AddImage(img);
//many to many, actually calls session.SaveOrUpdate(object) - also fails to insert img and m2m row.
repo.Update(session);
最后,这是 Nhib 编写的 Sql 日志。
NHibernate: select site0_.Id as Id3_, site0_.OwnerId as OwnerId3_, site0_.Layout
as Layout3_, site0_.Alias as Alias3_, site0_.CreatedUtc as CreatedUtc3_, site0_
.Domains as Domains3_, site0_.DefaultPageId as DefaultP7_3_, site0_.ResourceKey
as Resource8_3_ from site site0_ where site0_.Id=?p0 limit ?p1;?p0 = 2 [Type: In
t32 (0)], ?p1 = 1 [Type: Int32 (0)]
NHibernate: SELECT images0_.SiteId as SiteId1_, images0_.Id as Id1_, images0_.Id
as Id0_0_, images0_.Name as Name0_0_, images0_.SizeOnDisk as SizeOnDisk0_0_, im
ages0_.Hash as Hash0_0_, images0_.Width as Width0_0_, images0_.Height as Height0
_0_, images0_.Description as Descript7_0_0_, images0_.Caption as Caption0_0_, im
ages0_.CreatedUtc as CreatedUtc0_0_, images0_.ModifiedUtc as Modifie10_0_0_, ima
ges0_.ProcessedUtc as Process11_0_0_, images0_.ContentType as Content12_0_0_, im
ages0_.SiteId as SiteId0_0_ FROM image images0_ WHERE images0_.SiteId=?p0;?p0 =
2 [Type: Int32 (0)]
NHibernate: select photosessi0_.Id as Id2_, photosessi0_.AccessCode as AccessCod
e2_, photosessi0_.Description as Descript3_2_, photosessi0_.CreatedUtc as Create
dUtc2_, photosessi0_.ModifiedUtc as Modified5_2_, photosessi0_.EventDate as Even
tDate2_, photosessi0_.SiteId as SiteId2_ from photo_session photosessi0_ where p
hotosessi0_.Id=?p0 limit ?p1;?p0 = 25 [Type: Int32 (0)], ?p1 = 1 [Type: Int32 (0
)]
NHibernate: SELECT images0_.PhotoSessionId as PhotoSes2_1_, images0_.ImageId as
ImageId1_, image1_.Id as Id0_0_, image1_.Name as Name0_0_, image1_.SizeOnDisk as
SizeOnDisk0_0_, image1_.Hash as Hash0_0_, image1_.Width as Width0_0_, image1_.H
eight as Height0_0_, image1_.Description as Descript7_0_0_, image1_.Caption as C
aption0_0_, image1_.CreatedUtc as CreatedUtc0_0_, image1_.ModifiedUtc as Modifie
10_0_0_, image1_.ProcessedUtc as Process11_0_0_, image1_.ContentType as Content1
2_0_0_, image1_.SiteId as SiteId0_0_ FROM photo_session_image images0_ left oute
r join image image1_ on images0_.ImageId=image1_.Id WHERE images0_.PhotoSessionI
d=?p0;?p0 = 25 [Type: Int32 (0)]
Here's my scenario (after reading through Fluent's first project and automapping pages). I have nearly the same setup except some of my conventions differ from the default like table names and foreign key column names. I've provided manual overrides for the many-to-many relationship in question; one side is inverse and the other is not. For some reason, this is still not cascading the add. I can manually add the image alone. I can also successfully read the whole schema up and down the hierarchy.
I've tried leaving out the manual manyToMany mapping override; but I end up with a funky table name ImageToPhotoSession
in the select, which fails.
Any ideas?
I should mention I'm using:
Nhibernate 3
Fluent 1.2 (beta)
Config:
private readonly static ISessionFactory factory =
Fluently.Configure()
.Database(MySQLConfiguration.Standard.ShowSql().ConnectionString(o => o.FromConnectionStringWithKey("Default")))
.Mappings(m =>
m.AutoMappings.Add(AutoMap.AssemblyOf<Data.Image>(new TestMappingConfig())
.Override<Data.PhotoSession>(map => map
.HasManyToMany<Data.Image>(o => o.Images)
.Cascade.All()
.Table("photo_session_image"))
.Override<Data.Image>(map => map
.HasManyToMany(x => x.PhotoSessions)
.Cascade.All()
.Inverse()
.Table("photo_session_image"))
.Conventions.Add(
Table.Is(x => Inflector.Net.Inflector.Underscore(x.EntityType.Name)),
DefaultLazy.Always(),
DefaultCascade.All(),
ForeignKey.EndsWith("Id")
)))
.BuildSessionFactory();
Here are my data entities:
public class Image
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int SizeOnDisk { get; set; }
public virtual string Hash { get; set; }
public virtual int Width { get; set; }
public virtual int Height { get; set; }
public virtual string Description { get; set; }
public virtual string Caption { get; set; }
public virtual DateTime CreatedUtc { get; set; }
public virtual DateTime ModifiedUtc { get; set; }
public virtual DateTime ProcessedUtc { get; set; }
public virtual string ContentType { get; set; }
public virtual Site Site { get; set; }
public virtual IList<PhotoSession> PhotoSessions { get; private set; }
public Image()
{
this.PhotoSessions = new List<PhotoSession>();
}
}
public class PhotoSession
{
public virtual int Id { get; private set; }
public virtual string Description { get; set; }
public virtual DateTime CreatedUtc { get; set; }
public virtual DateTime ModifiedUtc { get; set; }
public virtual DateTime? EventDate { get; set; }
public virtual IList<Image> Images { get; private set; }
public virtual Site Site { get; set; }
public PhotoSession()
{
Images = new List<Image>();
}
public virtual void AddImage(Image data)
{
data.PhotoSessions.Add(this);
this.Images.Add(data);
}
}
Here's a snippet of code I'm trying to use.
var repo = new Repository();
var site = repo.All<Data.Site>().Where(o => o.Id == 2).FirstOrDefault();
var img = new Data.Image()
{
Name = "test",
Caption = "test",
ContentType = "image/jpeg",
CreatedUtc = DateTime.UtcNow,
Description = "test",
Hash = "test",
Height = 23,
ModifiedUtc = DateTime.UtcNow,
SizeOnDisk = 23,
Width = 23
};
site.AddImage(img);
//one to many, actually calls session.SaveOrUpdate(object) - fails to insert img
repo.Update(site);
//gets a sample session
var session = repo.All<Data.PhotoSession>().Where(o => o.Id == 25).FirstOrDefault();
session.AddImage(img);
//many to many, actually calls session.SaveOrUpdate(object) - also fails to insert img and m2m row.
repo.Update(session);
And finally, here's the Sql log written by Nhib.
NHibernate: select site0_.Id as Id3_, site0_.OwnerId as OwnerId3_, site0_.Layout
as Layout3_, site0_.Alias as Alias3_, site0_.CreatedUtc as CreatedUtc3_, site0_
.Domains as Domains3_, site0_.DefaultPageId as DefaultP7_3_, site0_.ResourceKey
as Resource8_3_ from site site0_ where site0_.Id=?p0 limit ?p1;?p0 = 2 [Type: In
t32 (0)], ?p1 = 1 [Type: Int32 (0)]
NHibernate: SELECT images0_.SiteId as SiteId1_, images0_.Id as Id1_, images0_.Id
as Id0_0_, images0_.Name as Name0_0_, images0_.SizeOnDisk as SizeOnDisk0_0_, im
ages0_.Hash as Hash0_0_, images0_.Width as Width0_0_, images0_.Height as Height0
_0_, images0_.Description as Descript7_0_0_, images0_.Caption as Caption0_0_, im
ages0_.CreatedUtc as CreatedUtc0_0_, images0_.ModifiedUtc as Modifie10_0_0_, ima
ges0_.ProcessedUtc as Process11_0_0_, images0_.ContentType as Content12_0_0_, im
ages0_.SiteId as SiteId0_0_ FROM image images0_ WHERE images0_.SiteId=?p0;?p0 =
2 [Type: Int32 (0)]
NHibernate: select photosessi0_.Id as Id2_, photosessi0_.AccessCode as AccessCod
e2_, photosessi0_.Description as Descript3_2_, photosessi0_.CreatedUtc as Create
dUtc2_, photosessi0_.ModifiedUtc as Modified5_2_, photosessi0_.EventDate as Even
tDate2_, photosessi0_.SiteId as SiteId2_ from photo_session photosessi0_ where p
hotosessi0_.Id=?p0 limit ?p1;?p0 = 25 [Type: Int32 (0)], ?p1 = 1 [Type: Int32 (0
)]
NHibernate: SELECT images0_.PhotoSessionId as PhotoSes2_1_, images0_.ImageId as
ImageId1_, image1_.Id as Id0_0_, image1_.Name as Name0_0_, image1_.SizeOnDisk as
SizeOnDisk0_0_, image1_.Hash as Hash0_0_, image1_.Width as Width0_0_, image1_.H
eight as Height0_0_, image1_.Description as Descript7_0_0_, image1_.Caption as C
aption0_0_, image1_.CreatedUtc as CreatedUtc0_0_, image1_.ModifiedUtc as Modifie
10_0_0_, image1_.ProcessedUtc as Process11_0_0_, image1_.ContentType as Content1
2_0_0_, image1_.SiteId as SiteId0_0_ FROM photo_session_image images0_ left oute
r join image image1_ on images0_.ImageId=image1_.Id WHERE images0_.PhotoSessionI
d=?p0;?p0 = 25 [Type: Int32 (0)]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
事实证明,我没有将操作包装在会话事务中。直接查看 Fluent NHibernates 网站上的示例:
在本例中,考虑到 IoC,
Repository
类在内部保留对ISession
和ITransaction
的引用。会话结束后,我建立了一个新事务。然后,我创建了Repository
类IDisposable
并在处置后提交了事务并关闭了会话。我不必引用Repository
之外的任何 Nhib 内容。所以最后,我的代码如下所示:
It turns out, I did not wrap my operations in a session transaction. Looking right at the sample on Fluent NHibernates website:
In this case with IoC in mind, the
Repository
class keeps a reference internally toISession
andITransaction
. Upon contruction of the session I established a new transaction. Then I made theRepository
classIDisposable
and upon disposing, I committed the transaction and closed the session. I shouldn't have to reference any Nhib stuff outside ofRepository
.So in the end, my code looks like: