web-dev-qa-db-ja.com

nhibernate:同じ識別子の値を持つ別のオブジェクトがすでにセッションに関連付けられています:2、エンティティ:

「会社」エンティティをmvcアプリケーションに保存しようとすると、次のエラーが発生します

同じ識別子の値を持つ別のオブジェクトが既にセッションに関連付けられています:2、エンティティ:

IOCコンテナを使用しています

private class EStoreDependencies : NinjectModule
    {
        public override void Load()
        {

            Bind<ICompanyRepository>().To<CompanyRepository>().WithConstructorArgument("session",
                                                                                       NHibernateHelper.OpenSession());
        }
    }

私の会社リポジトリ

public class CompanyRepository : ICompanyRepository
{
    private ISession _session;

    public CompanyRepository(ISession session)
    {
        _session = session;
    }    

    public void Update(Company company)
    {

        using (ITransaction transaction = _session.BeginTransaction())
        {

            _session.Update(company);
            transaction.Commit();
        }
    }

}

そしてセッションヘルパー

public class NHibernateHelper
{
    private static ISessionFactory _sessionFactory; 
    const string SessionKey = "MySession";


    private static ISessionFactory SessionFactory
    {
        get
        {
            if (_sessionFactory == null)
            {
                var configuration = new Configuration();
                configuration.Configure();
                configuration.AddAssembly(typeof(UserProfile).Assembly);
                configuration.SetProperty(NHibernate.Cfg.Environment.ConnectionStringName,
                                          System.Environment.MachineName);
                _sessionFactory = configuration.BuildSessionFactory();
            }
            return _sessionFactory;
        }
    }

    public static ISession OpenSession()
    {
        var context = HttpContext.Current;
        //.GetCurrentSession()

        if (context != null && context.Items.Contains(SessionKey))
        {
            //Return already open ISession
            return (ISession)context.Items[SessionKey];
        }
        else
        {
            //Create new ISession and store in HttpContext
            var newSession = SessionFactory.OpenSession();
            if (context != null)
                context.Items[SessionKey] = newSession;

            return newSession;
        }
    }
}

私のMVCアクション

    [HttpPost]
    public ActionResult Edit(EStore.Domain.Model.Company company)
    {

            if (company.Id > 0)
            {

                _companyRepository.Update(company);
                _statusResponses.Add(StatusResponseHelper.Create(Constants
                    .RecordUpdated(), StatusResponseLookup.Success));
            }
            else
            {
                company.CreatedByUserId = currentUserId;
               _companyRepository.Add(company);
            }


        var viewModel = EditViewModel(company.Id, _statusResponses);
        return View("Edit", viewModel);
    }
25
frosty

私はこれが少し遅れていることを知っていて、あなたはすでに解決策を見つけたかもしれませんが、多分他の人がそれから利益を得ることができます...

このエラーは、キャッシュに保存されているエンティティのインスタンスを更新しているときにnHibernateから発生します。基本的に、nHibernateはオブジェクトをロードするとキャッシュに保存するため、次の呼び出しではキャッシュからオブジェクトが取得されます。キャッシュに存在するインスタンスを更新すると、nHibernateがこのエラーをスローします。それ以外の場合は、ダーティリードが発生し、オブジェクトの古いコピーのロードに関して競合が発生する可能性があります。これを回避するには、次のようなEvictメソッドを使用して、オブジェクトをキャッシュから削除する必要があります。

public ActionResult Edit(EStore.Domain.Model.Company company) 
{ 

        if (company.Id > 0) 
        { 
            **ISession.Evict(company);**
            _companyRepository.Update(company);

お役に立てれば。

38
Claiton Lovato

@claitonlovatojrのハックを試しましたが、まだエラーを処理できませんでした。

私の場合、ISession.Update(obj)ISession.Merge(obj)呼び出しを置き換えるのが面倒でした。

リポジトリで、次を変更します。

public void Update(Company company)
{
    using (ITransaction transaction = _session.BeginTransaction())
    {
        //_session.Update(company);
        _session.Merge(company); // <-- this
        transaction.Commit();
    }
}

また、詳細については この答え を参照してください。

12
Joel

これに対する可能な解決策は、データベースからオブジェクトを読み取り、フィールドをオブジェクトにコピーして保存することです。 NHibernateセッションは、MVCモデルバインダーによってインスタンス化された着信オブジェクトについて何も知りません。

場合によっては、オブジェクト全体が表示されないか、View/ViewModelに渡されないことがあります。保存するときは、最初にNHibernateから読み取り、次に更新して保存する必要があります。

Company cOrig = _companyRepository.Get(company.Id);
cOrig.PropertyToUpdate = company.PropertyToUpdate;
... // Copy the properties to be updated.
// Save the freshly retrieved object! 
// Not the new object coming from the View which NHibernate Session knows nothing about.
_companyRepository.Update(cOrig);

これには、ViewModel/Classプロパティをドメインモデル/クラスに解析/マッピングする必要がありますが、多くの場合、ビューで更新するためにそれらすべてを必ずしも提示する必要はないので、とにかくそれを行う必要があります(部分的に空に保存することはできません)古いオブジェクトの上にあるオブジェクト)。

4
lko

より積極的な方法では、Clear()メソッドを使用できます

1
Ziv.Ti

私はこれに遭遇したばかりで、Claiton Lovatoの答えはうまくいきませんでした。しかしイコは働きました。こちらはもう少し堅牢なバージョンのイコです。欠点は、x2がデータベースにアクセスすることです。1つはGet用で、もう1つは挿入/更新用です。

これに対する可能な解決策は、データベースからオブジェクトを読み取り、フィールドをオブジェクトにコピーして保存することです。

public void Save(Company company)
{

    Company dbCompany = null;
    //update
    if (company.Id != 0)
    {
        dbCompany = _companyRepository.Get(company.Id);
        dbCompany.PropertyToUpdate = company.PropertyToUpdate;
    }
    //insert
    else
    {
        dbDefaultFreightTerm = company;
    }
    // Save either the brand new object as an insert
    // Or update the original dbCompany object with an update
    _companyRepository.SaveOrUpdate(company);
}
0
w00ngy