1

我正在尝试在流利的 NHibernate 中进行简单的一对多映射,但是我收到以下异常:

“NHibernate.TransientObjectException:对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例或将属性的级联操作设置为使其自动保存的内容。类型:Voter.Domain.Entities.VoteOption,实体:Voter.Domain.Entities .VoteOption"

我已经尝试过无数次使用 Cascade().All() - 但这没有区别。

请帮助我让这个级联工作!已经浪费了很多时间...

我有以下实体:

public class Vote 
{
    public Vote()
    {
        VoteOptions = new List<VoteOption>();
    }

    public virtual int Id { get; protected set; }
    public virtual Guid VoteReference { get; set; }
    public virtual string Title { get; set; }
    public virtual string Description { get; set; }
    public virtual DateTime ValidFrom { get; set; }
    public virtual DateTime ValidTo { get; set; }

    public virtual IList<VoteOption> VoteOptions { get; set; }

    public virtual void AddOption(VoteOption voteOption)
    {
        VoteOptions.Add(voteOption);
    }

    public virtual void AddOptions(List<VoteOption> options)
    {
        foreach (var option in options.Where(option => VoteOptionAlreadyExists(option) == false))
        {
            VoteOptions.Add(option);
        }
    }

    private bool VoteOptionAlreadyExists(VoteOption voteOption)
    {
        return VoteOptions.Any(x => x.Description == voteOption.Description);
    }
}

public class VoteOption
{
    public virtual int Id { get; protected set; }
    public virtual string LongDescription { get; set; }
    public virtual string Description { get; set; }
    public virtual Vote Vote { get; set; }
}

以及以下映射:

    public VoteMap()
    {
        Table("Vote");
        Id(x => x.Id).GeneratedBy.Identity().Column("Id");
        Map(x => x.VoteReference).Column("VoteReference");
        Map(x => x.Title).Column("Title").Not.Nullable();
        Map(x => x.Description).Column("Description").Not.Nullable();
        Map(x => x.ValidFrom).Column("ValidFrom").Not.Nullable();
        Map(x => x.ValidTo).Column("ValidTo").Not.Nullable();

        HasMany(x => x.VoteOptions).KeyColumn("Vote_Id").Cascade.All();
    }

public class VoteOptionMap : ClassMap<VoteOption>
{
    public VoteOptionMap()
    {
        Table("VoteOption");
        Id(x => x.Id).GeneratedBy.Identity().Column("Id");
        Map(x => x.Description).Column("Description").Not.Nullable();
        Map(x => x.LongDescription).Column("LongDescription").Not.Nullable();

        References(x => x.Vote).Column("Vote_Id").Cascade.All();

    }
}

以及以下 SQL Server 数据库表:

CREATE TABLE dbo.Vote
(
Id INT IDENTITY(1,1) PRIMARY KEY,
VoteReference UNIQUEIDENTIFIER NULL,
Title VARCHAR(500) NOT NULL,
[Description] VARCHAR(1000) NOT NULL,
ValidFrom DATETIME NOT NULL,
ValidTo DATETIME NOT NULL
)


CREATE TABLE dbo.VoteOption
(
Id INT IDENTITY(1,1) PRIMARY KEY,
Vote_Id INT NOT NULL,
[Description] VARCHAR(500) NOT NULL,
LongDescription VARCHAR(5000) NOT NULL
)

实现代码为:

public void Save()
    {
        var vote = new Vote
        {
            VoteReference = new Guid(),
            Title = "Events Vote",
            Description = "Which event would you like to see next?",
            ValidFrom = DateTime.Now.AddDays(-2),
            ValidTo = DateTime.Now.AddDays(3)
        };

        var options = new List<VoteOption>
                {
                    new VoteOption {Description = "What you want?", LongDescription = "Tell me all about it..."},
                    new VoteOption {Description = "Another option?", LongDescription = "Tell me some more..."}
                };

        vote.AddOptions(options);

        using (var session = sessionFactory().OpenSession())
        {
            using (var transaction = session.BeginTransaction())
            {
                //This works - but undermines cascade!
                //foreach (var voteOption in vote.VoteOptions)
                //{
                //    session.Save(voteOption);
                //}

                session.Save(vote);
                transaction.Commit();
            }
        }

    }

    private ISessionFactory sessionFactory()
    {
        var config = new Configuration().Configure();
        return Fluently.Configure(config)
            .Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Vote>()))
            .BuildSessionFactory();
    }
4

2 回答 2

1

我会说,如上所示的设置(流畅的映射)是可以的。换句话说,我现在看到的代码似乎有不同的问题,然后是顶部的异常。

HasMany 级联设置没问题,但我建议将其标记为反向(有关更多信息,请参见此处... NHibernate 不会尝试插入或更新此连接定义的属性...

HasMany(x => x.VoteOptions)
  .KeyColumn("Vote_Id")
  .Inverse()
  .Cascade.All();

此外,Reference在大多数情况下应该没有级联:References(x => x.Vote).Column("Vote_Id");

有了这个,并运行您的代码,我们现在应该收到SqlException:*无法将值 NULL 插入列 'Vote_Id'*

由于 TABLE dbo.VoteOption 定义:

...
Vote_Id INT NOT NULL, // must be filled even on a first INSERT

所以,最重要的变化应该在我们添加voteOptionVote集合(VoteOptions)的地方。我们总是应该/必须提供参考,即。voteOption.Vote = this;

public virtual void AddOption(VoteOption voteOption)
{
    VoteOptions.Add(voteOption);
    voteOption.Vote = this; // here we should/MUST reference back
}

public virtual void AddOptions(List<VoteOption> options)
{
    foreach (var option in options.Where(option => VoteOptionAlreadyExists(option) == false))
    {
        VoteOptions.Add(option);
        option.Vote = this; // here we should/MUST reference back
    }
}

经过这些调整,它应该可以正常工作

于 2013-11-10T19:22:27.097 回答
1

可以使用 Fluent NHibernate Automapping 约定全局设置级联选项。@Radim Köhler将项目添加到列表时也需要更正指出的问题。

使用全局约定:

添加一个约定,它可以是系统范围的,或者更多的限制。

DefaultCascade.All()

代码示例:

var cfg = new StoreConfiguration();
var sessionFactory = Fluently.Configure()
  .Database(/* database config */)
  .Mappings(m =>
    m.AutoMappings.Add(
      AutoMap.AssemblyOf<Product>(cfg)
          .Conventions.Setup(c =>
              {
                  c.Add(DefaultCascade.All());
              }
    )
  .BuildSessionFactory();

现在它会在保存时自动映射级联。


更多信息

自动映射维基

Table.Is(x => x.EntityType.Name + "Table")
PrimaryKey.Name.Is(x => "ID")
AutoImport.Never()
DefaultAccess.Field()
DefaultCascade.All()
DefaultLazy.Always()
DynamicInsert.AlwaysTrue()
DynamicUpdate.AlwaysTrue()
OptimisticLock.Is(x => x.Dirty())
Cache.Is(x => x.AsReadOnly())
ForeignKey.EndsWith("ID")

查看更多关于 Fluent NHibernate 自动映射约定

于 2014-02-13T07:03:49.593 回答