NBear是Teddy开发的快速开发框架,在之前的5个示例中,主要演示了主要的框架功能和业务领域模型不太复杂情形下忽略领域层的应用范例。但是,当业务模型相对复杂,单纯基于简单实体的强类型数据访问接口,可能就会使得太多的业务逻辑被分散到service或facade层,此时,我们就最好加一层独立的业务领域模型层来封装实体和强类型接口的使用。本文为您演示基于NBear v1.6.0中新增的NBear.Domain的领域模型设计示例。
下载源码及示例
您可以从这里下载NBear最新版本及Sample6源码
NBear.Domain
NBear.Domain主要为您提供了基类DomainModel和GuidKeyDomainModel,前者用于采用自增长ID主键的实体,后者用于采用Guid主键的实体。只需将他们作为你的领域类的基类,它就能提供最基本的领域类需要的CRUD等功能(包括Save, Delete, Find, FindAll等),您可以方便的以此为基础进行扩展。

DomainModel.cs
1
using System;
2
using System.Data;
3
using System.Data.Common;
4
using System.Collections.Generic;
5
using System.Text;
6
using NBear.Common;
7
using NBear.Data;
8
9
namespace NBear.Domain
10

{
11
public interface IDomainModel<IEntityType, IEntityViewType>
12
where IEntityType : IEntity
13
where IEntityViewType : IEntity
14
{
15
void Save();
16
void Save(DbTransaction tran);
17
void LoadFromEntity(IEntityViewType entity);
18
object ID
{ get; }
19
}
20
21
public abstract class DomainModel<IEntityType, IEntityViewType, DomainType> : IDomainModel<IEntityType, IEntityViewType>
22
where IEntityType : IEntity
23
where IEntityViewType : IEntity
24
where DomainType : IDomainModel<IEntityType, IEntityViewType>, new()
25
{
26
Protected Members#region Protected Members
27
28
protected IEntityType entityValue;
29
protected IEntityType entityValue2;
30
protected IEntityViewType entityViewValue;
31
32
protected DomainModel()
33
{
34
entityValue = Gateway.Create<IEntityType>();
35
entityValue2 = Gateway.Create<IEntityType>();
36
}
37
38
/**//// <summary>
39
/// override this method in sub class to customly load auto-created key column id if neccessary
40
/// by default, when saving a new domain model, the latest auto created ID will be loaded.
41
/// </summary>
42
protected virtual void LoadCreatedID(DbTransaction tran)
43
{
44
KeyValueCollection keyValues = entityValue.GetKeyValues();
45
46
keyValues[0] = Gateway.Db.ExecuteScalar(tran, CommandType.Text, string.Format("select max([{0}]) from [{1}]", keyValues.GetKeys()[0], typeof(IEntityType).Name));
47
}
48
49
protected virtual void DoUpdate(DbTransaction tran)
50
{
51
string[] exceptColumns = Gateway.CompareEntities<IEntityType>(entityValue, entityValue2);
52
if (exceptColumns.Length == NBear.Common.Entity<IEntityType>.GetKeys().Length)
53
{
54
//no columns are modified, so no need to update
55
return;
56
}
57
KeyValueCollection keyValues = entityValue.GetKeyValues();
58
if (tran == null)
59
{
60
Gateway.Update<IEntityType>(keyValues.GetKeys(exceptColumns), keyValues.GetValues(exceptColumns), ID);
61
}
62
else
63
{
64
Gateway.Update<IEntityType>(keyValues.GetKeys(exceptColumns), keyValues.GetValues(exceptColumns), ID, tran);
65
}
66
}
67
68
protected virtual void DoCreate(DbTransaction tran)
69
{
70
string exceptKeyColumn = Entity<IEntityType>.GetKeys()[0];
71
if (tran == null)
72
{
73
DbTransaction t = Gateway.BeginTransaction();
74
75
try
76
{
77
Gateway.Insert<IEntityType>(entityValue, t, exceptKeyColumn);
78
79
LoadCreatedID(t);
80
81
t.Commit();
82
}
83
catch
84
{
85
t.Rollback();
86
}
87
finally
88
{
89
Gateway.CloseTransaction(t);
90
}
91
}
92
else
93
{
94
Gateway.Insert<IEntityType>(entityValue, tran, exceptKeyColumn);
95
LoadCreatedID(tran);
96
}
97
}
98
99
#endregion
100
101
Properties#region Properties
102
103
public IEntityType EntityValue
104
{
105
get
106
{
107
return entityValue;
108
}
109
}
110
111
public IEntityViewType EntityViewValue
112
{
113
get
114
{
115
return entityViewValue;
116
}
117
}
118
119
public virtual object ID
120
{
121
get
122
{
123
return entityValue.GetKeyValues()[0];
124
}
125
}
126
127
#endregion
128
129
Basic CRUD#region Basic CRUD
130
131
public virtual void LoadFromEntity(IEntityViewType entityView)
132
{
133
if (entityView == null)
134
{
135
return;
136
}
137
entityValue = Gateway.ConvertEntity<IEntityViewType, IEntityType>(entityView);
138
entityValue2 = Gateway.ConvertEntity<IEntityViewType, IEntityType>(entityView);
139
}
140
141
public void Save()
142
{
143
Save(null);
144
}
145
146
public virtual void Save(DbTransaction tran)
147
{
148
if (ID == null || Convert.ToInt32(ID) == 0)
149
{
150
DoCreate(tran);
151
}
152
else
153
{
154
DoUpdate(tran);
155
}
156
157
LoadFromEntity(Gateway.Get<IEntityViewType>(ID));
158
}
159
160
public static DomainType Find(object id)
161
{
162
DomainType obj = new DomainType();
163
obj.LoadFromEntity(Gateway.Get<IEntityViewType>(id));
164
return obj;
165
}
166
167
public static DomainType[] FindAll(string orderBy)
168
{
169
return EntityViewArrayToDomainArray(Gateway.SelectAll<IEntityViewType>(orderBy));
170
}
171
172
public static DomainType[] EntityViewArrayToDomainArray(IEntityViewType[] entityViews)
173
{
174
DomainType[] objs = new DomainType[entityViews.Length];
175
for (int i = 0; i < objs.Length; i++)
176
{
177
DomainType obj = new DomainType();
178
obj.LoadFromEntity(entityViews[i]);
179
objs[i] = obj;
180
}
181
return objs;
182
}
183
184
public static void Delete(object id)
185
{
186
Gateway.Delete<IEntityType>(id);
187
}
188
189
#endregion
190
191
Gateway#region Gateway
192
193
private static NBear.Data.Facade.Gateway _Gateway = null;
194
195
public static NBear.Data.Facade.Gateway Gateway
196
{
197
get
198
{
199
return (_Gateway == null ? GatewayManager.DefaultGateway : _Gateway);
200
}
201
set
202
{
203
_Gateway = value;
204
}
205
}
206
207
#endregion
208
}
209
210
public abstract class DomainModel<IEntityType, DomainType> : DomainModel<IEntityType, IEntityType, DomainType>
211
where IEntityType : IEntity
212
where DomainType : IDomainModel<IEntityType, IEntityType>, new()
213
{
214
}
215
216
public abstract class GuidKeyDomainModel<IEntityType, IEntityViewType, DomainType> : DomainModel<IEntityType, IEntityViewType, DomainType>
217
where IEntityType : IEntity
218
where IEntityViewType : IEntity
219
where DomainType : IDomainModel<IEntityType, IEntityViewType>, new()
220
{
221
protected override void DoCreate(DbTransaction tran)
222
{
223
//create guid
224
entityValue.GetKeyValues()[0] = Guid.NewGuid().ToString();
225
226
if (tran == null)
227
{
228
Gateway.Insert<IEntityType>(entityValue);
229
}
230
else
231
{
232
Gateway.Insert<IEntityType>(entityValue, tran);
233
}
234
}
235
}
236
237
public abstract class GuidKeyDomainModel<IEntityType, DomainType> : GuidKeyDomainModel<IEntityType, IEntityType, DomainType>
238
where IEntityType : IEntity
239
where DomainType : IDomainModel<IEntityType, IEntityType>, new()
240
{
241
}
242
}
示例首先,还是需要用NBear.Tools.EntityGen生成实体,这里的数据库还是采用的之前的示例
Sample2中的数据库TestRelation.mdb。

Entities.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Text;
4
using NBear.Common;
5
6
namespace Sample6.Entities
7

{
8
public interface Group : IEntity
9
{
10
int ID
{ get; set; }
11
string Title
{ get; set; }
12
string Description
{ get; set; }
13
System.DateTime CreateTime
{ get; set; }
14
int ParentID
{ get; set; }
15
}
16
17
public interface Message : IEntity
18
{
19
int ID
{ get; set; }
20
int FromUserID
{ get; set; }
21
int ToUserID
{ get; set; }
22
string Title
{ get; set; }
23
string Content
{ get; set; }
24
System.DateTime SendTime
{ get; set; }
25
}
26
27
public interface User : IEntity
28
{
29
int ID
{ get; set; }
30
string Name
{ get; set; }
31
bool Gender
{ get; set; }
32
double Salary
{ get; set; }
33
}
34
35
public interface UserGroup : IEntity
36
{
37
int UserID
{ get; set; }
38
int GroupID
{ get; set; }
39
}
40
41
public interface vGroup : IEntity
42
{
43
int ID
{ get; set; }
44
string Title
{ get; set; }
45
string Description
{ get; set; }
46
System.DateTime CreateTime
{ get; set; }
47
int ParentID
{ get; set; }
48
int UserID
{ get; set; }
49
}
50
51
public interface vMessage : IEntity
52
{
53
int ID
{ get; set; }
54
int FromUserID
{ get; set; }
55
string FromUserName
{ get; set; }
56
int ToUserID
{ get; set; }
57
string ToUserName
{ get; set; }
58
string Title
{ get; set; }
59
string Content
{ get; set; }
60
System.DateTime SendTime
{ get; set; }
61
}
62
63
public interface vUser : IEntity
64
{
65
int ID
{ get; set; }
66
string Name
{ get; set; }
67
bool Gender
{ get; set; }
68
double Salary
{ get; set; }
69
int GroupID
{ get; set; }
70
}
71
}
接着就能定义领域模型了,只需简单的继承DomainModel,你的类就拥有了基本的CURD接口,如下面的Message类,你也可以扩展你的类,为你的领域类扩充复杂的领域功能,使用OneToMany类,操作与当前类关联的其他实体也非常便利,如这里的Group和User类。

Domains.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Text;
4
using NBear.Common;
5
using NBear.Data.Facade;
6
using NBear.Domain;
7
8
namespace Sample6.Domains
9

{
10
public class Group : DomainModel<Entities.Group, Entities.vGroup, Group>
11
{
12
private OneToMany<Entities.Group, Entities.vGroup, Entities.UserGroup, Entities.vUser> groupToUsers;
13
14
public override void LoadFromEntity(Sample6.Entities.vGroup entityView)
15
{
16
base.LoadFromEntity(entityView);
17
18
groupToUsers = new OneToMany<Entities.Group,Entities.vGroup,Entities.UserGroup,Entities.vUser>(Gateway.Db, "GroupID", entityValue, entityView);
19
}
20
21
public User[] FindUsersInGroup(string orderBy)
22
{
23
return (groupToUsers == null ? null : User.EntityViewArrayToDomainArray(groupToUsers.SelectMany(null, orderBy)));
24
}
25
26
public void AddUserToGroup(object userID)
27
{
28
if (groupToUsers == null)
29
{
30
return;
31
}
32
33
Entities.UserGroup userGroup = groupToUsers.CreateNewRelationEntity();
34
userGroup.UserID = (int)userID;
35
groupToUsers.InsertMany(userGroup);
36
}
37
38
public void DeleteUserFromGroup(object userID)
39
{
40
if (groupToUsers == null)
41
{
42
return;
43
}
44
45
groupToUsers.DeleteMany("[UserID] = @UserID", userID);
46
}
47
}
48
49
public class Message : DomainModel<Entities.Message, Entities.vMessage, Message>
50
{
51
}
52
53
public class User : DomainModel<Entities.User, Entities.vUser, User>
54
{
55
private OneToMany<Entities.User, Entities.vUser, Entities.Message, Entities.vMessage> fromUserToMessages;
56
private OneToMany<Entities.User, Entities.vUser, Entities.Message, Entities.vMessage> toUserToMessages;
57
58
public override void LoadFromEntity(Sample6.Entities.vUser entityView)
59
{
60
base.LoadFromEntity(entityView);
61
62
fromUserToMessages = new OneToMany<Entities.User, Entities.vUser, Entities.Message, Entities.vMessage>(Gateway.Db, "fromUserID", entityValue, entityView);
63
toUserToMessages = new OneToMany<Entities.User, Entities.vUser, Entities.Message, Entities.vMessage>(Gateway.Db, "toUserID", entityValue, entityView);
64
}
65
66
public Message[] FindSentMessages(string orderBy)
67
{
68
if (fromUserToMessages == null)
69
{
70
return null;
71
}
72
73
return Message.EntityViewArrayToDomainArray(fromUserToMessages.SelectMany(null, orderBy));
74
}
75
76
public Message[] FindReceivedMessages(string orderBy)
77
{
78
if (toUserToMessages == null)
79
{
80
return null;
81
}
82
83
return Message.EntityViewArrayToDomainArray(toUserToMessages.SelectMany(null, orderBy));
84
}
85
}
86
}
ok,领域模型定义完了,下面就可以方便的使用以上类了:

Usage.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Data.Common;
4
using System.Text;
5
using NBear.Domain;
6
using Sample6.Domains;
7
using NBear.Data.Facade;
8
9
namespace Sample6
10

{
11
public class Usage
12
{
13
public static void Main()
14
{
15
//first you should set GatewayManager.DefaultGateway for all Domain Models before using them
16
GatewayManager.DefaultGateway = new Gateway(DatabaseType.MsAccess, @"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=C:\Teddy\NBear\dist\Sample6\App_Data\TestRelation.mdb");
17
18
//if neccessary, you can set Specific Domain Models using specific Gateway instead of DefaultGateway
19
//Group.Gateway =
;
20
//Message.Gateway =
;
21
//User.Gateway =
;
22
23
//use Domain Models
24
25
//find group & users in group
26
Group group = Group.Find(1);
27
User[] usersInGroup = group.FindUsersInGroup("ID desc");
28
29
//modify and save group
30
//only modified properties will be in the update SQL which will be created behind domain model code
31
group.EntityValue.Description = "modified description";
32
group.Save();
33
34
//find user & delete from group
35
User user = usersInGroup[0];
36
Message[] sentMessages = user.FindSentMessages("SendTime desc");
37
group.DeleteUserFromGroup(user.ID);
38
39
//create new user & add to group
40
User newUser = new User();
41
newUser.EntityValue.Name = "new user";
42
newUser.EntityValue.Salary = 1000;
43
newUser.EntityValue.Gender = false;
44
newUser.Save();
45
group.AddUserToGroup(newUser.ID);
46
47
//modify user salary, save & send message in a transaction
48
DbTransaction tran = User.Gateway.BeginTransaction();
49
try
50
{
51
user.EntityValue.Salary = 99999;
52
user.Save(tran);
53
54
Message msg = new Message();
55
msg.EntityValue.Title = "new msg";
56
msg.EntityValue.Content = "salary changed";
57
msg.EntityValue.FromUserID = user.EntityValue.ID;
58
msg.EntityValue.ToUserID = 3;
59
msg.EntityValue.SendTime = DateTime.Now;
60
msg.Save(tran);
61
62
tran.Commit();
63
}
64
catch
65
{
66
tran.Rollback();
67
throw;
68
}
69
finally
70
{
71
User.Gateway.CloseTransaction(tran);
72
}
73
74
//if you want to access more user's value than its basic value, you can access user.EntityViewType, which is its view entity - vUser
75
int groupIDOfUser = user.EntityViewValue.GroupID;
76
}
77
}
78
}