0

子オブジェクトを変更してから親オブジェクトを保存しようとすると、ASP.NET WebForms アプリで Fluent Nhibernate に深刻な問題が発生します。

私のソリューションは現在2つのプロジェクトで構成されています:

  • コア : すべてのエンティティとリポジトリ クラスが配置されているクラス ライブラリ
  • Web サイト : 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 で次のコードを使用して、Web アプリで要求ごとに 1 つのセッションを使用しています。

    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"];
    }
}

これは、従業員のオブジェクト データ操作を処理するために使用するリポジトリ クラスの 1 つです。

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;
    }
}

さて、ここに私の問題があります。従業員を挿入しようとすると、すべて問題ありません。更新も完璧です...従業員の「TimeEntries」プロパティで参照されているTimeEntryオブジェクトの1つを更新していない限り...

例外が発生する場所は次のとおりです (Web プロジェクトの ASPX ファイル内)。

            var emp = new Employee(1);
            foreach (var timeEntry in emp.TimeEntries)
            {
                timeEntry.Length += 1;
            }
            emp.Save();

発生する例外は次のとおりです。

[NonUniqueObjectException: 同じ識別子の値を持つ別のオブジェクトが既にセッションに関連付けられていました: 1、エンティティ: Core.Entities.Employee]

基本的に、私がしようとするたびに

  1. 従業員をロードし、
  2. 保存された の 1 つを変更すると、TimeEntryその例外が発生します。

SaveOrUpdate()参考までに、リポジトリの をに置き換えてみましたMerge()。素晴らしい仕事をしましたが、 を使用してオブジェクトを作成するMerge()と、私のオブジェクトは ID セットを取得しません。

ISessionまた、リポジトリの各機能で を作成してフラッシュしようとしました。従業員のプロパティを読み込もうとするとすぐにTimeEntries例外が発生し、ISession が閉じられたため、オブジェクトを遅延読み込みできないと言って意味がありませんでした...

私は道に迷っており、助けていただければ幸いです。私はこれにまったく慣れていないので、私のリポジトリに関する提案も大歓迎です。

ありがとうございます!

4

2 に答える 2

1

このコード

    var emp = new Employee(1);
    foreach (var timeEntry in emp.TimeEntries)
    {
        timeEntry.Length += 1;
    }
    emp.Save();

コンストラクターを介して渡された ID 1 を使用して、新しい Employee オブジェクトを作成しています。データベースから Employee をロードする必要があり、ID 列を使用しているため、Employee オブジェクトで ID を設定できないようにする必要があります。また、新しい Employee には TimeEntries がなく、エラー メッセージは Employee インスタンスに問題があることを明確に示しています。

私はリポジトリ内のトランザクションのファンではなく、一般的なリポジトリのファンでもありません。なぜあなたの EmployeeRepository はジェネリックなのですか? そうじゃないかな

public class EmployeeRepository : IRepository<Employee>

あなたのコードは次のようになるはずです:

var repository = new EmployeeRepository(session);
var emp = repository.GetById(1);
foreach (var timeEntry in emp.TimeEntries)
{
    timeEntry.Length += 1;
}
repository.Save(emp);

個人的には、ISession を直接操作することを好みます。

using (var txn = _session.BeginTransaction())
{
    var emp = _session.Get<Employee>(1);
    foreach (var timeEntry in emp.TimeEntries)
    {
        timeEntry.Length += 1;
    }
    txn.Commit();
}
于 2013-04-07T14:45:23.887 に答える