下午好。
在我开始解释之前,我已经看过其他类似的问题,但微妙的差异(主要是设计目的)意味着这些答案中提供的解决方案并不适用于我。
我正在尝试创建一个'基础数据访问库'以供将来的项目使用,这样我就不必花时间在多个项目中编写相同的逻辑。目的是简单地导入这个库,并立即有能力从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过去的基本持久性做的很少。任何帮助或相关材料的链接非常赞赏。
感谢您的时间。
答案 0 :(得分:0)
我要做的第一件事是
_
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)
创建IEntityBase
(EntityBase
)的抽象实现,然后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);
}