Fluent Nhibernate:使用接口要求映射单个实体

时间:2010-08-28 11:44:08

标签: nhibernate fluent-nhibernate interface nhibernate-mapping persistence

下午好。

在我开始解释之前,我已经看过其他类似的问题,但微妙的差异(主要是设计目的)意味着这些答案中提供的解决方案并不适用于我。

我正在尝试创建一个'基础数据访问库'以供将来的项目使用,这样我就不必花时间在多个项目中编写相同的逻辑。目的是简单地导入这个库,并立即有能力从IBaseDao继承并立即拥有标准的“CRUD”能力。

因为(在编写这个基础库时)我无法确切知道我的业务对象是什么(Customer,FileStructure,Cat,Dog,Order等)我在我的基础库中定义了一个接口,所有业务对象都必须在它们可以与此库一起使用之前实现。这样的接口定义如下:

public interface IEntity
{
    /// Indicates weather this entity is used as test
    /// data or if it is a real-world entity.
    bool IsMockObject { get; set; }

    /// This is not intended for use in a 'real' application
    /// and is only used in testing.
    string ReadableName { get; set; }

    /// The unique identifier for this object.
    Guid Id { get; set; }
}

所以现在在“真正的”应用程序中,我打算得到类似于以下内容的东西:

public class Customer : IEntity
{
    public Customer()
    {

    }

    string name = "";
    public virtual String Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }

    private DateTime birthday = DateTime.Now;
    public virtual DateTime Birthday
    {
        get;
        set;
    }

    private List<Order> orders = new List<Order>();
    public virtual List<Order> Orders
    {
        get
        {
            return orders;
        }
        set
        {
            orders = value;
        }
    }

    #region IEntity Members

    private bool isMockObject;
    public virtual bool IsMockObject
    {
        get
        {
            return true;
        }
        set
        {
            isMockObject = value;
        }
    }

    private string readableName;
    public virtual string ReadableName
    {
        get
        {
            return readableName;
        }
        set
        {
            readableName = value;
        }
    }

    private Guid id;
    public virtual Guid Id
    {
        get
        {
            return id;
        }
        set
        {
            id = value;
        }
    }
    #endregion
}

现在问题就出现了。在我的单元测试期间,当我尝试保存“客户”时,我得到参数计数不匹配异常。我怀疑这个问题的原因是我的数据库中的Customer表不包含所有IEntity属性的列。但它包含Id列。到目前为止,我的CustomerMap看起来像这样:

public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {
        Table("Customer");

        // From IEntity
        Id(x => x.Id);

        Map(x => x.Name);
        Map(x => x.Birthday);

        // From IEntity
        Map(x => x.IsMockObject);

        // From IEntity
        Map(x => x.ReadableName);
        HasMany(x => x.Orders);
    }
}

我真正希望NHibernate在保存时要保存在Customer表中,Customer所有的所有属性以及IEntity.Id属性都保存在一个单独的表中(如果IsMockEntity是true)MockEntitiy模拟对象的Id及其可读名称。

我发现ClassMap目前很难理解,因为我对NHibernate过去的基本持久性做的很少。任何帮助或相关材料的链接非常赞赏。

感谢您的时间。

4 个答案:

答案 0 :(得分:0)

我要做的第一件事是

  1. 将列表设置为IList; NHibernate需要能够将自己的可枚举类型注入到对象中,而不是C#定义的List。
  2. 确保NHibernate能够读取和写入所有属性,即不在映射中设置默认值。如果需要,可以在构造函数(或其他地方)中执行此操作。
  3. _

    public class Customer
    {
        public Customer()
        {
            IsMockObject = true;
            Orders = new List<Order>();
        }
    
        public virtual Guid Id { get; set; }
        public virtual string Name { get; set; }
        public virtual DateTime Birthday { get; set; }  
    
        // Protected: the user can't modify this!
        public virtual bool IsMockObject { get; protected set; }
    
        public virtual string ReadableName { get; set; }
    
        // Needs to be an IList<T>, NOT a regular List<T>.
        public virtual IList<Order> Orders { get; set; }
    }
    

    _

    我认为你的ClassMap没有任何问题,但我先改变这些东西,然后看看是什么。如果它仍然无效,请通知我们抛出的新异常。

答案 1 :(得分:0)

好的我已修改代码以反映您建议的更改,并且还进行了一些“挖掘”以尝试解决此问题。我现在有以下数据库结构:

表客户
   ID uniqueidentifier
  名称 varchar(50)
  生日日期时间

表格订单
   ID uniqueidentifier
   CustomerId uniqueidentifier
  价格浮动

表MockEntities
   ID uniqueidentifier
   EntityId uniqueidentifier
   ReadableName nvarchar(MAX)

因此,下面的ClassMap是。从我设法从其他来源收集,我想在这里实现的是某种联合。至于天气,它是一个外部或内部......我不确定。

 public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {
        Table("Customer");

        Id(x => x.Id);
        Map(x => x.Name);
        Map(x => x.Birthday);
        Join("MockEntities", me =>
            {
                Id<Guid>("Id");
                me.Map(xx => xx.ReadableName);
                me.Map(xx => xx.Id, "EntityId");
            });
        HasMany(x => x.Orders).Cascade.All().Not.LazyLoad();

    }
}

Order的ClassMap看起来几乎完全相同,但为了完整起见,我将把它包括在这篇文章中。

public class OrderMap : ClassMap<Order>
{
    public OrderMap()
    {
        Table("Order");
        Id(x => x.Id);
        Map(x => x.Price);
        Map(x => x.CustomerId, "CustomerId");
        Join("MockEntities", me =>
            {
                Id<Guid>("Id");
                me.Map(xx => xx.ReadableName);
                me.Map(xx => xx.Id, "EntityId");
            });
    }
}

但是,现在,当我运行测试时,我收到以下错误:

`System.Data.SqlServerCeException:列名无效。 [节点名称(如果有)=,列名称=客户ID]

以防它对任何人都有用,来自NUnit GUI的堆栈跟踪:

at System.Data.SqlServerCe.SqlCeCommand.CompileQueryPlan()
at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior, String method, ResultSetOptions options)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteNonQuery()
at NHibernate.AdoNet.AbstractBatcher.ExecuteNonQuery(IDbCommand cmd)
at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor session)
at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Object obj, ISessionImplementor session)
at NHibernate.Action.EntityInsertAction.Execute()
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
at NHibernate.Engine.ActionQueue.ExecuteActions()
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
at NHibernate.Impl.SessionImpl.Flush()
at NHibernate.Transaction.AdoTransaction.Commit()
at MySolution.DataAccess.ConnectionProviders.NHibernateConnection.Create[TEntity](TEntity objectToCreate) in MySolution.DataAccess\ConnectionProviders\NHibernateConnection.cs:line 154
at MySolution.Testing.DataAccess.Connection.Testing_Connections.Function_NHibernateCreateNestedEntity()

最后,NHibernate正在使用的SQL:

NHibernate: INSERT INTO Customer (Name, Birthday, Id) VALUES (@p0, @p1, @p2);@p0 = 'David', @p1 = 03/09/2010 07:28:22, @p2 = 68fb7dd7-5379-430f-aa59-9de6007b2d56
NHibernate: INSERT INTO MockEntities (ReadableName, EntityId, Customer_id) VALUES (@p0, @p1, @p2);@p0 = 'Function_NHibernateCreateNestedEntity1', @p1 = 00000000-0000-0000-0000-000000000000, @p2 = 68fb7dd7-5379-430f-aa59-9de6007b2d56
07:28:28,851 ERROR [TestRunnerThread] AbstractBatcher [(null)]- Could not execute command: INSERT INTO MockEntities (ReadableName, EntityId, Customer_id) VALUES (@p0, @p1, @p2)
The column name is not valid. [ Node name (if any) = ,Column name = Customer_id ]

在这个问题上,我害怕我有点超出我的深度。在我试图加入映射工作之前,系统正在保存一个Customer对象及其包含的Order集合。然而,现在好像它正在期待一个我没有在任何地方定义的列,即“客户ID”。

非常感谢任何输入,谢谢。

答案 2 :(得分:0)

public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {
        Table("Customer");

        Id(x => x.Id);

        ...

此时,您正在定义正在爆炸的Customer.Id字段,因此您需要检查一些事项。

首先,您需要确保它存在。 (在你的情况下,我认为它可能会这样做)

其次,看看你的SQL我们有一个问题......

@p2 = 68fb7dd7-5379-430f-aa59-9de6007b2d56

看起来这是一个字符串,但它不包含在单引号中。

@p2 = '68fb7dd7-5379-430f-aa59-9de6007b2d56'

这可能是因为NHibernate错误地认为我们正在处理一个整数,我认为你可以通过这样调整你的CustomerMap来解决这个问题。

public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {
        Table("Customer");

        Id<Guid>(x => x.Id);

        ...

如果我没有指出数字ID几乎肯定比其他任何东西更好,那么这个答案就不会完整,因为它们会导致更快的查询,更好的索引并且是一个公认的约定。但是,这应该会让你再次感动。

答案 3 :(得分:0)

创建IEntityBaseEntityBase)的抽象实现,然后Customer继承EntityBase(因此实现IEntityBase)。然后为EntityBase创建一个地图,为Customer创建一个子类地图。然后,您应该拥有一个EntityBase表和一个Customer表,其中包含与EntityBase表相关的列EntityBase_Id。你会发现EntityBase需要另一个属性(这样就可以通过插入一个新行并设置另一个属性来在数据库中生成Id)所以我通常会添加一个Created时间戳或类似的。

public interface IEntityBase
{
    int Id { get; set; }
    DateTime? Created { get; set; }
}

public abstract class EntityBase : IEntityBase
{
    public int Id { get; set; }
    public DateTime? Created { get; set ;}
}

public class Customer : EntityBase
{
    public string Name { get; set; }
}

public class EntityBaseMap : ClassMap<EntityBase>
{
    Id(x => x.Id);
    Map(x => x.Created);
}

public class CustomerMap : SubclassMap<Customer>
{
    Map(x => x.Name);
}