0

我在尝试解决模型中的子实体时遇到了一系列问题,我使用 nhibernate 进行持久性,使用 Windsor 进行 ioc 和 automapper 进行映射。

我已经以多种方式对此进行了攻击,并且几乎总是在此过程中被阻止,任何帮助将不胜感激。

我对下面代码的问题是,当我尝试通过以下方式更新页面布局时。(假设只有 layout-id 正在改变)

        var page = _pageRepository.Get(model.Id);
        Mapper.Map(model, page);

        using (ITransaction tran = _sessionFactory.BeginTransaction())
        {
            _pageRepository.Update(page);
            tran.Commit();
        }

我得到一个很好的错误说,

具有相同标识符值的不同对象已与会话相关联:对于布局模型。

现在我尝试了: - 将设施更改为 perwebrequest(然后说会话已关闭) - 尝试在获取缓存后从缓存中删除布局(错误如上) - 我尝试在解析器中获取现有会话(上下文错误)

我应该如何进一步解决这个问题?肯定不能这么难!我哪里错了?非常感谢。

这是所有重要的部分。

我有一个这样的模型:

public class ContentPage : Page
{ 
    public virtual Layout Layout { get; set; } 
}

我使用持久化工具来管理我的休眠会话,如下所示:

        Kernel.Register(
            Component.For<ISessionFactory>()
                .UsingFactoryMethod(_ => config.BuildSessionFactory()),

            Component.For<ISession>()
                .UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession())
                .LifestylePerThread() <-- IMPORTANT FOR LATER.
            );

我的映射是这样的:

        CreateMap<BlaViewModel, ContentPage>()
            .ForMember(dest => dest.DateModified, src => src.MapFrom(x => DateTime.UtcNow)) 
            .ForMember(x => x.Layout, x => x.ResolveUsing<EntityResolver<Layout>>().FromMember(y => y.Layout_Id));

最后我的解析器是这样的:

public class EntityResolver<T> : ValueResolver<Guid, T> where T : EntityBase
{
    private readonly ISession _session;

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

    protected override T ResolveCore(Guid id)
    {
        var entity = _session.Get<T>(id); 
        return entity;
    }
}
4

3 回答 3

1

异常主要来自您在 Session 中创建了一个具有现有 Id 的新对象。在您的情况下,AutoMapper 很可能会这样做。

如何在 ContentPage 地图中配置 Layout 属性?如果你默认使用它,那么 AutoMapper 会创建一个新的 Layout 对象并将其设置为 Id,它不是从 Session 加载的。然后保存这个对象会导致异常。

所以你需要自定义 Layout 属性映射规则,如果是已经存在的模型,从 Session(Repository) 中检索,并设置它的值(通过 AutoMapper 或手动),那么会话的状态是正确的。

ContentPage 的 AutoMapper 配置可能类似于:

Mapper.CreateMap<VPage, ContentPage>()
    ....
    .ForMember(des=>des.Layout, opt=>opt.MapFrom(src=>GetLayout(src))) //customize Layout
    ....;

在您的 GetLayout 函数中,它就像:

private Layout GetLayout(VPage page)
{
    var layout = page.LayoutId == 0? new Layout() : _layoutRepository.Get(page.LayoutId); //avoid new Layout object with existed Id
    .......

    return layout;
}

更重要的是,您最好不要使用 AutoMapper 从 DTO 转换域模型,请参阅此说明

更新:很抱歉没有看到您的 EntityResolver,请尝试使用您的 LayoutRepository 来检索它。

于 2012-06-21T07:19:18.190 回答
0

My guess is that the resolver is using another session to get the layout.

// sample code
var layout1 = Resolve(1);

session.Attach(layout1);  // now contains layout 1

var layout2 = Resolve(1);

session.Attach(layout2);  // error: already contains layout with id 1


public Layout Resolve(int id)
{
    using (var session = OpenSession())
    {
        return GetNewSession.Get<Layout>(1);
    }
}

use the same session to resolve connected entities

于 2012-06-21T06:42:51.390 回答
0

几个小时后以典型的方式解决了这个问题,并在国际奥委会上学到了一些艰难的教训。

在上面的代码中,你可以看到我注册了我的 ISession 来解析如下

 Kernel.Register(
        Component.For<ISessionFactory>()
            .UsingFactoryMethod(_ => config.BuildSessionFactory()),

        Component.For<ISession>()
            .UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession())
            .LifestylePerThread() <-- IMPORTANT FOR LATER.
        );

这是我的问题的开始,主要是因为这是每个线程,几乎所有不同的点都会解决一个新的会话。以会话的多个实例结束。(因此错误)

一旦我将其更改为情况有所.LifestylePerWebRequest()好转,但我仍然遇到了一些会话问题。

最终弄清楚这个 Session 正在通过所有层(通过构造器的 IOC),所以我的管理器层、存储库层以及它正在使用的任何地方,都需要更改为作为 PerWebRequest 安装。

喜欢:

container.Register(Classes.FromAssemblyContaining<Repository>()
                               .Where(Component.IsInSameNamespaceAs<Repository>())
                               .WithService
                               .DefaultInterfaces()
                               .LifestylePerWebRequest());

和:

        container.Register(Component.For<EntityResolver<Layout>>().ImplementedBy<EntityResolver<Layout>>().LifestylePerWebRequest());

通过正确使用我的 IOC,最终达到了只有一个会话被假脱机(PerWebRequest)并且问题消失的地步。

好的。希望对其他关注相同问题的人有所帮助。

于 2012-06-21T22:23:28.950 回答