MySoft.Data发现系列一:实体的解耦
自老毛开始发布他的MySoft.Data系列组件,也陆续开始写Mysoft.Data使用系列的文章,我也尝试着写一点自己在研究和使用MySoft.Data过程中的一些心得。
本章介绍Mysoft.Data在作为一个ORM组件应用在系统中时,如何把MySoft.Data生成的实体转换成“干净”的实体,以实现系统和ORM之间的解耦。
大家都知道,一般ORM组件会有他们自己的特定的实体,例如EF的实体,或者其他ORM组件的实体,要么通过Attribute标记,要么xml文档配置,总而言之就是要把实体的结构跟数据表结构联系起来,MySoft.Data也不例外,大家来看它生成的实体:
2 /// 表名:Category 主键列:CategoryID
3 /// </summary>
4 [SerializableAttribute()]
5 public partial class Category : Entity
6 {
7
8 protected Int32 _CategoryID;
9
10 protected String _Name;
11
12 protected Int32 _UserID;
13
14 public Int32 CategoryID
15 {
16 get
17 {
18 return this._CategoryID;
19 }
20 set
21 {
22 this.OnPropertyValueChange(_.CategoryID, _CategoryID, value);
23 this._CategoryID = value;
24 }
25 }
26
27 public String Name
28 {
29 get
30 {
31 return this._Name;
32 }
33 set
34 {
35 this.OnPropertyValueChange(_.Name, _Name, value);
36 this._Name = value;
37 }
38 }
39
40 public Int32 UserID
41 {
42 get
43 {
44 return this._UserID;
45 }
46 set
47 {
48 this.OnPropertyValueChange(_.UserID, _UserID, value);
49 this._UserID = value;
50 }
51 }
52
53 /// <summary>
54 /// 获取实体对应的表名
55 /// </summary>
56 protected override Table GetTable()
57 {
58 return new Table<Category>("Category");
59 }
60
61 /// <summary>
62 /// 获取实体中的标识列
63 /// </summary>
64 protected override Field GetIdentityField()
65 {
66 return _.CategoryID;
67 }
68
69 /// <summary>
70 /// 获取实体中的主键列
71 /// </summary>
72 protected override Field[] GetPrimaryKeyFields()
73 {
74 return new Field[] {
75 _.CategoryID};
76 }
77
78 /// <summary>
79 /// 获取列信息
80 /// </summary>
81 protected override Field[] GetFields()
82 {
83 return new Field[] {
84 _.CategoryID,
85 _.Name,
86 _.UserID};
87 }
88
89 /// <summary>
90 /// 获取列数据
91 /// </summary>
92 protected override object[] GetValues()
93 {
94 return new object[] {
95 this._CategoryID,
96 this._Name,
97 this._UserID};
98 }
99
100 /// <summary>
101 /// 给当前实体赋值
102 /// </summary>
103 protected override void SetValues(IRowReader reader)
104 {
105 if ((false == reader.IsDBNull(_.CategoryID)))
106 {
107 this._CategoryID = reader.GetInt32(_.CategoryID);
108 }
109 if ((false == reader.IsDBNull(_.Name)))
110 {
111 this._Name = reader.GetString(_.Name);
112 }
113 if ((false == reader.IsDBNull(_.UserID)))
114 {
115 this._UserID = reader.GetInt32(_.UserID);
116 }
117 }
118
119 public override int GetHashCode()
120 {
121 return base.GetHashCode();
122 }
123
124 public override bool Equals(object obj)
125 {
126 if ((obj == null))
127 {
128 return false;
129 }
130 if ((false == typeof(Category).IsAssignableFrom(obj.GetType())))
131 {
132 return false;
133 }
134 if ((((object)(this)) == ((object)(obj))))
135 {
136 return true;
137 }
138 return false;
139 }
140
141 public class _
142 {
143
144 /// <summary>
145 /// 表示选择所有列,与*等同
146 /// </summary>
147 public static AllField All = new AllField<Category>();
148
149 /// <summary>
150 /// 字段名:CategoryID - 数据类型:Int32
151 /// </summary>
152 public static Field CategoryID = new Field<Category>("CategoryID");
153
154 /// <summary>
155 /// 字段名:Name - 数据类型:String
156 /// </summary>
157 public static Field Name = new Field<Category>("Name");
158
159 /// <summary>
160 /// 字段名:UserID - 数据类型:Int32
161 /// </summary>
162 public static Field UserID = new Field<Category>("UserID");
163 }
164 }
我们来看看其实Category实体无非也就那么几个属性
2 {
3 [PrimaryKey]
4 int CategoryID { get; }
5 string Name { get; set; }
6 int UserID { get; set; }
7 }
如果使用MySoft.Data的实体,那就意味着你离不开MySoft.Data框架。
而实际项目中我们一定不会这么做(简单的项目就不说了),那么如何如何使用简单的“失血”实体和这个ORM中的实体项目转换呢,其实MySoft.Data里已经提供了灰常简单的方法,我们先来看看MySoft.Data里定义的接口:
2 /// 列表转换
3 /// </summary>
4 public interface IListConvert<T>
5 {
6 /// <summary>
7 /// 返回另一类型的列表
8 /// </summary>
9 /// <typeparam name="TOutput"></typeparam>
10 /// <returns></returns>
11 ISourceList<TOutput> ConvertTo<TOutput>();
12
13 /// <summary>
14 /// 返回另一类型的列表(输出为接口)
15 /// </summary>
16 /// <typeparam name="TOutput"></typeparam>
17 /// <typeparam name="IOutput"></typeparam>
18 /// <returns></returns>
19 ISourceList<IOutput> ConvertTo<TOutput, IOutput>();
20
21 /// <summary>
22 /// 将当前类型转成另一种类型
23 /// </summary>
24 /// <typeparam name="TOutput"></typeparam>
25 /// <param name="converter"></param>
26 /// <returns></returns>
27 ISourceList<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter);
28 }
我们再来看看,它的继承关系
2 /// 数据源接口
3 /// </summary>
4 public interface ISourceList<T> : IListConvert<T>, IArrayList<T>
5 {
6 /// <summary>
7 /// 转换成SourceTable
8 /// </summary>
9 /// <returns></returns>
10 ISourceTable ToTable();
11
12 /// <summary>
13 /// 返回MemoryFrom
14 /// </summary>
15 /// <returns></returns>
16 MemoryFrom<T> ToMemory();
17
18 /// <summary>
19 /// 添加一个实体
20 /// </summary>
21 /// <param name="item"></param>
22 /// <returns></returns>
23 new ISourceList<T> Add(T item);
24
25 /// <summary>
26 /// 添加一个实体
27 /// </summary>
28 /// <param name="item"></param>
29 /// <returns></returns>
30 ISourceList<T> AddRange(IEnumerable<T> collection);
31
32 #region 字典操作
33
34 /// <summary>
35 /// 返回字典
36 /// </summary>
37 /// <typeparam name="TResult"></typeparam>
38 /// <param name="groupName"></param>
39 /// <returns></returns>
40 IDictionary<TResult, IList<T>> ToGroupList<TResult>(string groupName);
41
42 /// <summary>
43 /// 返回字典
44 /// </summary>
45 /// <typeparam name="TResult"></typeparam>
46 /// <param name="groupField"></param>
47 /// <returns></returns>
48 IDictionary<TResult, IList<T>> ToGroupList<TResult>(Field groupField);
49
50 #endregion
51 }
ISourceList<T>继承自IListConvert<T>和IArrayList<T>,IArrayList<T>是啥,IArrayList<T>是一个实现了IList<T>的东东,因此,实现ISourceList<T>,一定会实现IList<T>,我们就可以使用可爱的IList<T>了。
但实际上,MySoft.Data里的SourceList实现了ISourceList,不仅如此,SourceList还(间接)实现了List<T>,大家看它的继承关系
2 /// 数组列表
3 /// </summary>
4 /// <typeparam name="T"></typeparam>
5 public class SourceList<T> : ArrayList<T>, ISourceList<T>
6 {
7 public SourceList() { }
8
9 /// <summary>
10 /// 实例化SourceList
11 /// </summary>
12 /// <param name="list"></param>
13 public SourceList(IList<T> list)
14 : base(list)
15 { }
16
17 #region IArrayList<T> 成员
18
19 /// <summary>
20 /// 转换成SourceTable
21 /// </summary>
22 /// <returns></returns>
23 public ISourceTable ToTable()
24 {
25 return new SourceTable(this.GetDataTable());
26 }
27
28 /// <summary>
29 /// 返回MemoryFrom
30 /// </summary>
31 /// <returns></returns>
32 public MemoryFrom<T> ToMemory()
33 {
34 return new MemoryFrom<T>(this.GetDataTable());
35 }
36
37 /// <summary>
38 /// 返回另一类型的列表(用于实体的解耦))
39 /// </summary>
40 /// <typeparam name="TOutput"></typeparam>
41 /// <returns></returns>
42 public ISourceList<TOutput> ConvertTo<TOutput>()
43 {
44 return this.ConvertAll<TOutput>(p => DataUtils.ConvertType<T, TOutput>(p));
45 }
46
47 /// <summary>
48 /// 将当前类型转成另一种类型
49 /// </summary>
50 /// <typeparam name="TOutput"></typeparam>
51 /// <param name="converter"></param>
52 /// <returns></returns>
53 public new ISourceList<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter)
54 {
55 IList<TOutput> list = base.ConvertAll<TOutput>(converter);
56 return new SourceList<TOutput>(list);
57 }
58
59 /// <summary>
60 /// 添加一个实体
61 /// </summary>
62 /// <param name="item"></param>
63 /// <returns></returns>
64 public new ISourceList<T> Add(T item)
65 {
66 base.Add(item);
67 return this;
68 }
69
70 /// <summary>
71 /// 添加一个实体
72 /// </summary>
73 /// <param name="item"></param>
74 /// <returns></returns>
75 public new ISourceList<T> AddRange(IEnumerable<T> collection)
76 {
77 base.AddRange(collection);
78 return this;
79 }
80
81 /// <summary>
82 /// 返回另一类型的列表(输入为类、输出为接口,用于实体的解耦)
83 /// </summary>
84 /// <typeparam name="TOutput"></typeparam>
85 /// <typeparam name="IOutput"></typeparam>
86 /// <returns></returns>
87 public ISourceList<IOutput> ConvertTo<TOutput, IOutput>()
88 {
89 if (!typeof(TOutput).IsClass)
90 {
91 throw new MySoftException("TOutput必须是Class类型!");
92 }
93
94 if (!typeof(IOutput).IsInterface)
95 {
96 throw new MySoftException("IOutput必须是Interface类型!");
97 }
98
99 //进行两次转换后返回
100 return ConvertTo<TOutput>().ConvertTo<IOutput>();
101 }
102
103 #region 字典操作
104
105 /// <summary>
106 /// 返回字典
107 /// </summary>
108 /// <typeparam name="TResult"></typeparam>
109 /// <param name="groupField"></param>
110 /// <returns></returns>
111 public IDictionary<TResult, IList<T>> ToGroupList<TResult>(Field groupField)
112 {
113 return ToGroupList<TResult>(groupField.PropertyName);
114 }
115
116 /// <summary>
117 /// 返回字典
118 /// </summary>
119 /// <typeparam name="TResult"></typeparam>
120 /// <param name="groupName"></param>
121 /// <returns></returns>
122 public IDictionary<TResult, IList<T>> ToGroupList<TResult>(string groupName)
123 {
124 IDictionary<TResult, IList<T>> group = new Dictionary<TResult, IList<T>>();
125 if (this.Count == 0) return group;
126
127 foreach (T t in this)
128 {
129 object obj = DataUtils.GetPropertyValue(t, groupName);
130 TResult value = DataUtils.ConvertValue<TResult>(obj);
131 if (!group.ContainsKey(value))
132 {
133 group.Add(value, new SourceList<T>());
134 }
135 group[value].Add(t);
136 }
137
138 return group;
139 }
140
141 #endregion
142
143 /// <summary>
144 /// 转换成DataTable
145 /// </summary>
146 /// <returns></returns>
147 private DataTable GetDataTable()
148 {
149 #region 对list进行转换
150
151 DataTable dt = new DataTable();
152 dt.TableName = typeof(T).Name;
153
154 T obj = DataUtils.CreateInstance<T>();
155 PropertyInfo[] plist = obj.GetType().GetProperties();
156 foreach (PropertyInfo p in plist)
157 {
158 Type propertyType = p.PropertyType;
159 if (!CanUseType(propertyType)) continue; //shallow only
160
161 //nullables must use underlying types
162 if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
163 propertyType = Nullable.GetUnderlyingType(propertyType);
164 //enums also need special treatment
165 if (propertyType.IsEnum)
166 propertyType = Enum.GetUnderlyingType(propertyType); //probably Int32
167
168 dt.Columns.Add(p.Name, propertyType);
169 }
170
171 if (this.Count == 0) return dt;
172
173 foreach (T t in this)
174 {
175 DataRow dtRow = dt.NewRow();
176 foreach (PropertyInfo p in plist)
177 {
178 object value = DataUtils.GetPropertyValue(t, p.Name);
179 dtRow[p.Name] = value == null ? DBNull.Value : value;
180 }
181 dt.Rows.Add(dtRow);
182 }
183
184 #endregion
185
186 return dt;
187 }
188
189 private static bool CanUseType(Type propertyType)
190 {
191 //only strings and value types
192 if (propertyType.IsArray) return false;
193 if (!propertyType.IsValueType && propertyType != typeof(string)) return false;
194 return true;
195 }
196
197 #endregion
198 }
看来,我们不用担心兼容问题了,它就是List。
说了这么多废话,怎么使用它转成干净的实体呢??
项目中,我们可以把实体定义成接口,或者直接实体也行,怎么用,来看看这几个方法,这是今天我在ben的开源blog,xpress看到的
2 /// 获取评论列表
3 /// </summary>
4 /// <param name="status">状态</param>
5 /// <param name="pageNumber">页码</param>
6 /// <param name="pageSize">分页大小</param>
7 /// <returns></returns>
8 public IList<IComments> GetList(CommentStatus status, int pageNumber, int pageSize)
9 {
10 return repository.GetSession().From<Comments>()
11 .Where(Comments._.CommentStatus == status)
12 .OrderBy(Comments._.CommentTimeGMT.Desc)
13 .GetPage(pageSize)
14 .ToList(pageNumber).ConvertTo<CommentsModel, IComments>();//这里转化了成了SourceList<IComments>,而由于SourceList实现了IList<T>,因此兼容IList<IComments>了
15 }
16
17
18 //再来看这几个个扩展方法
19
20 /// <summary>
21 /// 转换成实体
22 /// </summary>
23 /// <typeparam name="TEntity"></typeparam>
24 /// <param name="entity"></param>
25 /// <returns></returns>
26 public static TEntity ToEntity<TEntity>(this DsJian.Interface.Model.IEntity entity)
27 where TEntity : MySoft.Data.Entity
28 {
29 return new MySoft.Data.SourceList<DsJian.Interface.Model.IEntity>(entity).ConvertTo<TEntity>()[0];
30 }
31
32 /// <summary>
33 /// 转换成实体
34 /// </summary>
35 /// <typeparam name="TEntity"></typeparam>
36 /// <param name="entity"></param>
37 /// <returns></returns>
38 public static TEntity ToEntity<TEntity>(this MySoft.Data.Entity entity)
39 {
40 return new MySoft.Data.SourceList<MySoft.Data.Entity>(entity).ConvertTo<TEntity>()[0];
41 }
42
43 /// <summary>
44 /// 转换成接口
45 /// </summary>
46 /// <typeparam name="TEntity"></typeparam>
47 /// <typeparam name="IEntity"></typeparam>
48 /// <param name="entity"></param>
49 /// <returns></returns>
50 public static IEntity ToIEntity<TEntity, IEntity>(this MySoft.Data.Entity entity)
51 where IEntity : DsJian.Interface.Model.IEntity
52 {
53 return new MySoft.Data.SourceList<MySoft.Data.Entity>(entity).ConvertTo<TEntity, IEntity>()[0];
54 }
我不用说,大家也该知道怎么用了。
OK,我是个不怎么会写东西的人,文章中可能有什么胡言乱语的云云,请大家爱护环境,留住你们手中紧攥的臭鸡蛋,谢谢!