12

データベースに 3 つのテーブルがあります。

  1. プロジェクト (ID、名前)
  2. タグ(id、名前)
  3. ProjectsTagss (id、projectId、tagid)

ご覧のとおり、ProjectsTags テーブルはブリッジ テーブルです。

これが私の流暢なnhibernateマッピングです

ProjectMap.cs:

 Map(x => x.Name).Not.Nullable();
 HasMany(x => x.ProjectsTags).AsBag().Inverse()
    .Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);

ProjectsTagsMap.cs:

 References(x => x.Project).Not.Nullable();
 References(x => x.Tag).Not.Nullable();

TagMap.cs:

  Map(x => x.Name).Not.Nullable();

ご覧のとおり、これまでタグ テーブルを他のものにリンクしていませんでした。タグとそのタグが使用される頻度を示すレポートを生成する必要があるため、タグから ProjectsTag に結合する必要があります。この行をタグマップに追加してみました:

 HasMany(x => x.ProjectsTags).AsBag().Inverse()
    .Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);

しかし、タグ オブジェクトの名前を更新してコミットすると、次のエラーが発生します。

cascade="all-delete-orphan" を持つコレクションは、所有エンティティ インスタンスによって参照されなくなりました

タグテーブルを更新するだけで、この nhibernate 例外を引き起こす可能性のある、追加したものに問題があることを誰かが見ることができますか? 繰り返しますが、私の目標は次のようなことができるようにすることです:

 Tag.ProjectTags.Count();

要求された追加のコードを次に示します。

私のタグクラス:

 public class Tag
{
    public virtual IList<ProjectTag> ProjectTags { get; set; }
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }
}
4

2 に答える 2

19

コードのどこかで、プロジェクトドメインの元のコレクションの参照を解除する必要があります。あなたのコードは次のようになっていると思います。

var project = Session.Get<Project>();
project.ProjectsTags = new List<ProjectsTags> { someProjectsTagsInstance };
Session.Save(project);

この場合、代わりにこれを行う必要があります。

var project = Session.Get<Project>();
project.ProjectsTags.Clear();
project.ProjectsTags.Add(someProjectsTagsInstance);
Session.Save(project);
于 2011-05-04T12:56:27.923 に答える
7

コレクションは変更されていませんが、NH はまだ変更されていると考えることができます。このような問題は、ゴースト アップデートが原因である可能性があります。NHibernate 3.0 クックブック、Jason Dentler (184 ページ) から: "。

コレクションのゴースト更新は、次のようなコードによって発生する可能性があります。

public class Tag
{
    private IList<ProjectTag> projectsTags;

    public virtual IEnumerable<ProjectTag> ProjectsTags
    {
        get
        {
            return new ReadOnlyCollection<ProjectTag>(projectsTags);
        }

        set
        {
            projectsTags = (IList<ProjectTag>)value;
        }
    }
}

ProjectsTags プロパティは読み取り専用ラッパーでコレクションを返すため、クライアント コードはコレクションに要素を追加したり、コレクションから要素を削除したりできません。

タグの名前が変更されていない場合でも、エラーが表示されます。

private void GhostTagUpdate(int id)
{
    using (var session = OpenSession())
    {
        using (var transaction = session.BeginTransaction())
        {
            var tag = session.Get<Tag>(id);

            transaction.Commit();
        }
    }
}

ProjectsTags コレクションは、ゴースト更新を回避するために、CamelCaseField アクセス戦略でマップする必要があります。

HasMany(x => x.ProjectsTags)
    .Access.CamelCaseField()
    .AsBag().Inverse().Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);

ともかく...

あなたの協会は悪魔のように複雑なようです。ProjectsTags テーブルにタグの ID とプロジェクトの ID のみを含める必要がある場合は、FNH 多対多双方向マッピングを使用する方が簡単です。

public class Tag2Map : ClassMap<Tag2>
{
    public Tag2Map()
    {
        Id(x => x.Id);
        Map(x => x.Name);
        HasManyToMany(x => x.Projects)
            .AsBag()
            .Cascade.None()
            .Table("ProjectsTags")
            .ParentKeyColumn("TagId")
            .ChildKeyColumn("ProjectId");
    }
}

public class Project2Map : ClassMap<Project2>
{
    public Project2Map()
    {
        Id(x => x.Id);
        Map(x => x.Name);
        HasManyToMany(x => x.Tags)
            .AsBag()
            .Cascade.None()
            .Inverse()
            .Table("ProjectsTags")
            .ParentKeyColumn("ProjectId")
            .ChildKeyColumn("TagId");
    }
}

モデルに ProjectTag エンティティは必要ありません。タグが使用された回数は、次の 2 つの方法で取得できます。

直接的な方法: tag.Projects.Count()- ただし、データベースからすべてのプロジェクトを取得します。

クエリ方法:

var tag = session.Get<Tag2>(tagId);
var count = session.Query<Project2>().Where(x => x.Tags.Contains(tag)).Count();
于 2011-05-04T21:22:26.123 に答える