Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/302.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用NHibernate时如何处理不同的实体实例?_C#_Nhibernate_Fluent Nhibernate_Identity_Equality - Fatal编程技术网

C# 使用NHibernate时如何处理不同的实体实例?

C# 使用NHibernate时如何处理不同的实体实例?,c#,nhibernate,fluent-nhibernate,identity,equality,C#,Nhibernate,Fluent Nhibernate,Identity,Equality,ORMs和对象标识有一个众所周知的问题。就ORM而言,如果实体具有相同的ID,则它们是相等的。当然,这不适用于被视为不存在的临时实例 但就OO代码而言,如果对象引用同一实例,则认为它们是相等的。也就是说,除非覆盖等于和/或== 这很好,但在实践中又意味着什么呢?下面是一个非常简单的域模型示例: namespace TryHibernate.Example { public abstract class Entity { public int Id { get; set; } } pub

ORMs和对象标识有一个众所周知的问题。就ORM而言,如果实体具有相同的ID,则它们是相等的。当然,这不适用于被视为不存在的临时实例

但就OO代码而言,如果对象引用同一实例,则认为它们是相等的。也就是说,除非覆盖
等于
和/或
==

这很好,但在实践中又意味着什么呢?下面是一个非常简单的域模型示例:

namespace TryHibernate.Example
{
public abstract class Entity
{
    public int Id { get; set; }
}

public class Employee : Entity
{
    public string Name { get; set; }

    public IList<Task> Tasks { get; set; }

    public Employee()
    {
        Tasks = new List<Task>();
    }
}

public class Task : Entity
{
    public Employee Assignee { get; set; }

    public Job Job { get; set; }
}

public class Job : Entity
{
    public string Description { get; set; }
}
} // namespace
namespace TryHibernate.Example
{
公共抽象类实体
{
公共int Id{get;set;}
}
公共类雇员:实体
{
公共字符串名称{get;set;}
公共IList任务{get;set;}
公职人员()
{
任务=新列表();
}
}
公共类任务:实体
{
公共雇员受让人{get;set;}
公共作业作业{get;set;}
}
公共类作业:实体
{
公共字符串说明{get;set;}
}
}//名称空间
下面是使用它的示例代码:

using (ISessionFactory sessionFactory = Fluently.Configure()
    .Database(SQLiteConfiguration.Standard.UsingFile("temp.sqlite").ShowSql())
    //.Cache(c => c.UseSecondLevelCache().UseQueryCache().ProviderClass<HashtableCacheProvider>())
    .Mappings(m => m.AutoMappings.Add(
        AutoMap.AssemblyOf<Entity>()
            .Where(type => type.Namespace == typeof(Entity).Namespace)
            .Conventions.Add(DefaultLazy.Never())
            .Conventions.Add(DefaultCascade.None())
            //.Conventions.Add(ConventionBuilder.Class.Always(c => c.Cache.ReadWrite()))
        ).ExportTo("hbm")
    ).ExposeConfiguration(c => new SchemaExport(c).Create(true, true))
    .BuildSessionFactory())
{
    Job job = new Job() { Description = "A very important job" };
    Employee empl = new Employee() { Name = "John Smith" };
    Task task = new Task() { Job = job, Assignee = empl };
    using (ISession db = sessionFactory.OpenSession())
    using (ITransaction t = db.BeginTransaction())
    {
        db.Save(job);
        db.Save(empl);
        empl.Tasks.Add(task);
        db.Save(task);
        t.Commit();
    }
    IList<Job> jobs;
    using (ISession db = sessionFactory.OpenSession())
    {
        jobs = db.QueryOver<Job>().List();
    }
    IList<Employee> employees;
    using (ISession db = sessionFactory.OpenSession())
    {
        employees = db.QueryOver<Employee>().List();
    }
    jobs[0].Description = "A totally unimportant job";
    Console.WriteLine(employees[0].Tasks[0].Job.Description);
}
使用(ISessionFactory sessionFactory=fluntly.Configure()
.Database(SQLiteConfiguration.Standard.UsingFile(“temp.sqlite”).ShowSql())
//.Cache(c=>c.UseSecondLevelCache().UseQueryCache().ProviderClass())
.Mappings(m=>m.AutoMappings.Add(
自动映射
.Where(type=>type.Namespace==typeof(Entity.Namespace)
.Conventions.Add(DefaultLazy.Never())
.Conventions.Add(DefaultCascade.None())
//.Conventions.Add(ConventionBuilder.Class.Always(c=>c.Cache.ReadWrite()))
).出口至(“hbm”)
).ExposeConfiguration(c=>newschemaexport(c).Create(true,true))
.BuildSessionFactory())
{
Job Job=new Job(){Description=“一项非常重要的工作”};
Employee Employee=new Employee(){Name=“John Smith”};
任务Task=newtask(){Job=Job,assignment=emp};
使用(ISession db=sessionFactory.OpenSession())
使用(ITransaction t=db.BeginTransaction())
{
db.Save(作业);
db.Save(emp);
emp.Tasks.Add(任务);
db.Save(任务);
t、 提交();
}
国际劳工组织的工作;
使用(ISession db=sessionFactory.OpenSession())
{
jobs=db.QueryOver().List();
}
IList员工;
使用(ISession db=sessionFactory.OpenSession())
{
employees=db.QueryOver().List();
}
作业[0]。Description=“完全不重要的作业”;
Console.WriteLine(员工[0]。任务[0]。职务。说明);
}
当然,它打印了“一项非常重要的工作”。启用二级缓存(注释掉)不会改变它,尽管在某些情况下会减少数据库命中率。显然这是因为NHibernate缓存数据,而不是对象实例

当然,在这里重写相等/散列代码没有帮助,因为不是相等引起问题。事实上,我这里有两个相同的例子

这都很好,但如何处理呢?有几种选择,但对我来说都不太有吸引力:

  • 引入一个中间服务层,将实例缓存在哈希表中,并在从存储库加载实体图后遍历它们。我真的不喜欢它,因为它需要大量的工作,容易出错,听起来像是我在做ORM的工作。如果我想这样做,我宁愿手动实现整个持久性,但我没有

  • 引入一个聚合根并从DB中提取一次,而不是提取几个部分。它可以工作,因为我的应用程序相对简单,我可以处理整个图形。事实上,无论如何我都在和它合作。但我不喜欢这个,因为它引入了不必要的实体。工作就是工作,员工就是员工。当然,我可以将上帝实体命名为“组织”之类的东西。我不喜欢它的另一个原因是,如果数据将来增长,它可能会变得笨拙。例如,我可能希望归档旧作业和任务

  • 每件事都使用一个会话。现在,我正在打开和关闭会话,因为我需要加载/存储某些内容。我可以使用单个会话,而NHibernate的标识映射将保证引用标识(只要我不使用延迟加载)。这似乎是最好的选择,但应用程序可能会运行一段时间(这是一个WPF桌面应用程序),我不喜欢让会话打开太长时间

  • 手动更新所有实例。例如,如果我想更改作业描述,我调用一些服务方法来搜索具有相同ID的作业实例并更新它们。这可能会变得非常混乱,因为该服务必须能够访问基本上所有的东西,基本上成为一种上帝服务


  • 我还错过了其他选择吗?令我惊讶的是,关于这个问题的信息如此之少。我能找到的最好的方法是,但它只处理平等问题。

    好的,这是我的想法。基本上,这是选项3,有一些调整。我的回购类现在看起来像这样:

    public class Repo : IDisposable
    {
        private ISessionFactory sessionFactory;
        private ISession session;
    
        public Repo(ISessionFactory sessionFactory)
        {
            this.sessionFactory = sessionFactory;
            this.session = sessionFactory.OpenSession();
            session.Disconnect();
        }
    
        public void Dispose()
        {
            try
            {
                session.Dispose();
            }
            finally
            {
                sessionFactory.Dispose();
            }
        }
    
        public void Save(Entity entity)
        {
            using (Connection connection = new Connection(session))
            using (ITransaction t = session.BeginTransaction())
            {
                session.Save(entity);
                t.Commit();
            }
        }
    
        public IList<T> GetList<T>() where T : Entity
        {
            using (Connection connection = new Connection(session))
            {
                return session.QueryOver<T>().List();
            }
        }
    
        private class Connection : IDisposable
        {
            private ISession session;
    
            internal Connection(ISession session)
            {
                this.session = session;
                session.Reconnect();
            }
    
            public void Dispose()
            {
                session.Disconnect();
            }
        }
    }
    
    公共类回购:IDisposable
    {
    私人ISessionFactory sessionFactory;
    非公开会议;
    公开回购(ISessionFactory sessionFactory)
    {
    this.sessionFactory=sessionFactory;
    this.session=sessionFactory.OpenSession();
    session.Disconnect();
    }
    公共空间处置()
    {
    尝试
    {
    session.Dispose();
    }
    最后
    {
    sessionFactory.Dispose();
    }
    }
    公共作废保存(实体)
    {
    使用(连接=新连接(会话))
    使用(ITransaction t=session.BeginTransaction())
    {
    session.Save(实体);
    t、 提交();
    }
    }
    public IList GetList(),其中T:Entity
    {
    使用(连接)
    
    using (Repo db = new Repo(sessionFactory))
    {
        Job job = new Job() { Description = "A very important job" };
        Employee empl = new Employee() { Name = "John Smith" };
        Task task = new Task() { Job = job, Assignee = empl };
        db.Save(job);
        db.Save(empl);
        empl.Tasks.Add(task);
        db.Save(task);
        IList<Job> jobs;
        jobs = db.GetList<Job>();
        IList<Employee> employees;
        employees = db.GetList<Employee>();
        jobs[0].Description = "A totally unimportant job";
        Console.WriteLine(employees[0].Tasks[0].Job.Description);
    }