Fluent Nhibernate:将单个实体与接口需求映射
下午好。
在开始解释之前,我已经查看了其他类似的问题,但细微的差异(主要是设计目的)意味着这些答案中提供的解决方案不适用于我。
我正在尝试创建一个“基础数据访问库”以供未来的项目使用,这样我就不必花时间在多个项目中编写相同的逻辑。目的是简单地导入这个库,并立即具有从 IBaseDao 继承的能力,并从一开始就立即具有标准的“CRUD”功能。
因为(在编码这个基础库时)我无法确切地知道我的业务对象是什么(客户、文件结构、猫、狗、订单等),我在我的基础库中定义了一个接口,所有业务对象都必须先实现该接口可以与该库一起使用。这样的接口定义如下:
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 属性然后将模拟对象的 Id 及其可读名称保存在一个名为(如果 IsMockEntity 为 true)MockEntitiy 的单独表中。
我发现 ClassMap 目前很难理解,因为我对 NHibernate 过去的基本持久性做了很少的工作。任何帮助或相关材料的链接非常表示赞赏。
感谢您抽出时间。
Good afternoon.
Before I begin my explanation, I have had a look at other similar questions but the subtle differences (mainly in purpose of design) mean that the solutions provided in these answers to not apply to me.
I am attempting to create a 'Base data access library' for use with future projects so that I do not have to spend my time coding the same logic across multiple projects. The intention is to simply import this library and instantly have the ability to inherit from a IBaseDao and instantly have standard 'CRUD' abilities from the get-go.
Because (when coding this base library) I have no way of knowing exactly what my business objects will be (Customer, FileStructure, Cat, Dog, Order ect) I have defined an interface within my base library which all business objects must implement before they can be used with this library. Such an interface is defined as follows:
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; }
}
So now in the 'real' application I intend to have something similar to the following:
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
}
Now here is where the issue arises. During my unit tests, when i try and save a 'Customer' I get a parameter count mismatch exception. I suspect the cause of this issue is that the Customer table within my database does not contain columns for all the IEntity properties. It does however contain an Id column. So far, my CustomerMap looks like this:
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);
}
}
What I actually want NHibernate to do when saving is save in the Customer table, all the properties unique to Customer
as well as the IEntity.Id
property then save in a seperate table called (if IsMockEntity is true) MockEntitiy the Id of the mock object and its readable name.
I find ClassMap's pretty difficult to understand at the moment as I have done very little with NHibernate past basic persistence. Any help or links to relevant material greatly appreciated.
Thank you for your time.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我要做的第一件事是
_
_
我认为您的 ClassMap 没有任何问题,但我会先更改这些内容,然后看看发生了什么。如果仍然不起作用,请通知我们抛出的新异常。
The first thing I would do is
_
_
I don't think there's any problems with your ClassMap, but I'd change these things first and then see what's up. If it still doesn't work, do notify us of the new exception thrown.
好的,我已经修改了代码以反映您建议的更改,并且还进行了更多“挖掘”以尝试解决此问题。我现在有以下数据库结构:
TABLE Customer
Id 唯一标识符
名称 varchar(50)
生日日期
时间表顺序
Id 唯一标识符
客户 ID 唯一标识符
价格 float
TABLE MockEntities
Id 唯一标识符
EntityId 唯一标识符
ReadableName nvarchar(MAX)
以及以下 ClassMap。根据我从其他来源收集到的信息,我在这里试图实现的是某种连接。至于天气,它是外部的还是内部的……我不确定。
Order 的 ClassMap 看起来几乎相同,但为了完整起见,我将把它包含在这篇文章中。
但是,现在,当我运行测试时,出现以下错误:
`System.Data.SqlServerCeException:列名无效。 [ 节点名称(如果有) = ,列名称 = 客户 id ]
以防万一它对任何人都有用,来自 NUnit GUI 的堆栈跟踪:
最后,NHibernate 正在使用的 SQL:
恐怕我有点超出了我的深度当谈到这个问题时。在我尝试让映射加入工作之前,系统很好地保存了 Customer 对象及其包含的 Order 集合。然而现在,它似乎需要一个我没有在任何地方定义的列,即“客户 ID”。
任何意见都非常感谢,谢谢。
Ok I have modified the code to reflect your suggested changes and have also done some more 'digging' to try and solve this issue. I now have the following database structure:
TABLE Customer
Id uniqueidentifier
Name varchar(50)
Birthday datetime
TABLE Order
Id uniqueidentifier
CustomerId uniqueidentifier
Price float
TABLE MockEntities
Id uniqueidentifier
EntityId uniqueidentifier
ReadableName nvarchar(MAX)
and thus, the following ClassMap's. From what I have managed to gather from other sources, what I am trying to achieve here is a join of some kind. As to weathers it is an outer or inner...im not sure.
The ClassMap for Order looks almost identical but for the sake of completeness I shall include it in this post.
However, now, when I run my tests I am given the following error:
`System.Data.SqlServerCeException: The column name is not valid. [ Node name (if any) = ,Column name = Customer id ]
Just in case it is useful to anyone, a stack trace from the NUnit GUI:
And finally, the SQL that NHibernate is using:
Im afraid im somewhat out of my depth when it comes to this issue. Previous to me trying to get this joining in the mappings working, the system was saving a Customer object and its contained Order collection very well. Now however, it seems as if it is expecting a column I have not defined anywhere, namely "Customer id".
Any input greatly appreciated, thank you.
此时,您正在定义正在爆炸的 Customer.Id 字段,因此您需要检查一些事项。
首先,您需要确保它存在。 (在你的情况下,我认为可能是这样)
其次,看看你的SQL,我们有一个问题......
看起来这是一个字符串,但它没有用单引号括起来。
这可能是因为 NHibernate 错误地认为我们正在处理一个整数,我认为你可以通过像这样调整你的 CustomerMap 来解决这个问题。
如果我没有指出数字 id 几乎肯定比其他任何东西都好,那么这个答案就不完整,因为它们会导致更快的查询、更好的索引,并且是一种可接受的约定。然而,这应该会让你的事情再次进展。
At this point, you are defining the Customer.Id field that is blowing up, so you need to check a few things.
Firstly, you need to make sure it exists. (In your case I think it probably does)
Secondly, looking at your SQL we have a problem...
It looks like this is a string, but it isn't wrapped in single quotes.
This is probably because NHibernate is under the mistaken impression that we are dealing with an integer, which I think you can fix by adjusting your CustomerMap like this.
This answer wouldn't be complete if I didn't point out that numeric ids are almost certainly better than anything else, as they result in faster queries, better indexing and are an accepted convention. However, this should get things moving again for you.
创建
IEntityBase
(EntityBase
) 的抽象实现,然后让Customer
继承EntityBase
(因此实现IEntityBase )。然后为
EntityBase
创建一个映射,并为Customer
创建一个子类映射。然后,您应该有一个 EntityBase 表和一个 Customer 表,其中的 EntityBase_Id 列与 EntityBase 表相关。您会发现 EntityBase 需要另一个属性(以便可以通过插入新行并设置另一个属性在数据库中生成 Id),因此我通常添加一个Created
时间戳或类似的属性。Create an abstract implementation of
IEntityBase
(EntityBase
) then haveCustomer
inheritEntityBase
(and therefore implementIEntityBase
). Then create a map forEntityBase
and a subclass map forCustomer
. You should then have an EntityBase table and a Customer table with a column EntityBase_Id that relates to the EntityBase table. You will find that EntityBase will need another property (so that the Id can be generated in the database by inserting a new row and setting the another property) so I usually add aCreated
timestamp or similar.