WCF开发日志 -- OEA里面的WCF设计
一、摘要
场景:
最近公司的项目中用到了WCF 就想着分析OEA里的WCF设计,吸取一下高手的经验 。
类图:
今天在这里主要是分析OEA里的WCF设计(也叫分布式业务对象数据门户)。先看一下OEA类库
二、本文大纲
a、摘要 。
b、本文大纲 。
c、WCF接口设计。
d、WCF 交互设计 。
e、数据门户设计 。
f、业务对象知识补充 。
三、WCF接口设计
关于WCF接口的相关知识可以看以前写WCF 开发日志 -- WCF契约设计
消息契约(MessageContract) 在 WCF 开发日志 -- WCF契约设计 有提到一点,在网上和MSDN上有更详细的说明了,这里就不详细描述了。
WcfResponse , FetchRequest , UpdateRequest上面类图对应有三个文件,其实这三个文件的内容都差不多,只是职责不同。
四、WCF 交互设计
服务端实现:
红色部分就是WCF适配到数据门户来进行与数据库交互否实现业务逻辑。1: /// <summary>2: /// 使1用?WCF实现的统一的数据门户3: ///4: /// 标记了ConcurrencyMode.Multiple 来表示多线程进行5: /// </summary>6: [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]7: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]8: public class WcfPortal : IWcfPortal9: {10: /// <summary>11: /// Get an existing business object.12: /// </summary>13: /// <param name="request">The request parameter object.</param>14: public WcfResponse Fetch(FetchRequest request)15: {
16: OEA.Server.DataPortalFacade portal = new OEA.Server.DataPortalFacade();17: object result;18: try19: {20: result = portal.Fetch(request.ObjectType, request.Criteria, request.Context);
21: }
22: catch (Exception ex)23: {
24: result = ex;
25: }
26: return new WcfResponse(result);27: }
28:
29: /// <summary>30: /// Update a business object.31: /// </summary>32: /// <param name="request">The request parameter object.</param>33: public WcfResponse Update(UpdateRequest request)34: {
35: OEA.Server.DataPortalFacade portal = new OEA.Server.DataPortalFacade();36: object result;37: try38: {39: result = portal.Update(request.Object, request.Context);
40: }
41: catch (Exception ex)42: {
43: result = ex;
44: }
45: return new WcfResponse(result);46: }
47: }
48:
客服端直接使用WCF的代理类ChannelFactory实现1: /// <summary>2: /// Implements a data portal proxy to relay data portal3: /// calls to a remote application server by using WCF.4: /// </summary>5: public class WcfProxy : OEA.DataPortalClient.IDataPortalProxy,IDataPortalServer6: {7: #region IDataPortalProxy Members8:
9: /// <summary>10: /// Gets a value indicating whether the data portal11: /// is hosted on a remote server.12: /// </summary>13: public bool IsServerRemote14: {
15: get { return true; }16: }
17:
18: #endregion19:
20: #region IDataPortalServer Members21:
22: private string _endPoint = "WcfDataPortal";23:
24: /// <summary>25: /// Gets or sets the WCF endpoint used26: /// to contact the server.27: /// </summary>28: /// <remarks>29: /// The default value is WcfDataPortal.30: /// </remarks>31: protected string EndPoint32: {
33: get34: {35: return _endPoint;36: }
37: set38: {39: _endPoint = value;40: }
41: }
42:
43: /// <summary>44: /// Returns an instance of the channel factory45: /// used by GetProxy() to create the WCF proxy46: /// object.47: /// </summary>48: protected virtual ChannelFactory<IWcfPortal> GetChannelFactory()49: {
50: return new ChannelFactory<IWcfPortal>(_endPoint);51: }
52:
53: /// <summary>54: /// Returns the WCF proxy object used for55: /// communication with the data portal56: /// server.57: /// </summary>58: /// <param name="cf">59: /// The ChannelFactory created by GetChannelFactory().60: /// </param>61: protected virtual IWcfPortal GetProxy(ChannelFactory<IWcfPortal> cf)62: {
63: return cf.CreateChannel();64: }
65:
66: /// <summary>67: /// Called by <see cref="DataPortal" /> to load an68: /// existing business object.69: /// </summary>70: /// <param name="objectType">Type of business object to create.</param>71: /// <param name="criteria">Criteria object describing business object.</param>72: /// <param name="context">73: /// <see cref="Server.DataPortalContext" /> object passed to the server.74: /// </param>75: public DataPortalResult Fetch(Type objectType, object criteria, DataPortalContext context)76: {
77: ChannelFactory<IWcfPortal> cf = GetChannelFactory();78: IWcfPortal svr = GetProxy(cf);79: WcfResponse response = null;80: try81: {82: response =
83: svr.Fetch(new FetchRequest(objectType, criteria, context));84: if (cf != null)85: cf.Close();
86: }
87: catch88: {89: cf.Abort();
90: throw;91: }
92:
93: object result = response.Result;94: if (result is Exception)95: throw (Exception)result;96: return (DataPortalResult)result;97: }
98:
99: /// <summary>100: /// Called by <see cref="DataPortal" /> to update a101: /// business object.102: /// </summary>103: /// <param name="obj">The business object to update.</param>104: /// <param name="context">105: /// <see cref="Server.DataPortalContext" /> object passed to the server.106: /// </param>107: public DataPortalResult Update(object obj, DataPortalContext context)108: {
109: ChannelFactory<IWcfPortal> cf = GetChannelFactory();110: IWcfPortal svr = GetProxy(cf);111: WcfResponse response = null;112: try113: {114: response =
115: svr.Update(new UpdateRequest(obj, context));116: if (cf != null)117: cf.Close();
118: }
119: catch120: {121: cf.Abort();
122: throw;123: }
124:
125:
126: object result = response.Result;127: if (result is Exception)128: throw (Exception)result;129: return (DataPortalResult)result;130: }
131:
132: #endregion133: }134:
IWcfPortal接口 和 IDataPortalServer接口 介绍 其实这两个接口都提供了 Fetch Update1: /// <summary>2: /// Defines the service contract for the WCF data3: /// portal.4: /// </summary>5: [ServiceContract(Namespace = "http://ws.lhotka.net/WcfDataPortal")]6: public interface IWcfPortal7: {8: /// <summary>9: /// Get an existing business object.10: /// </summary>11: /// <param name="request">The request parameter object.</param>12: [OperationContract]13: [UseNetDataContract]14: WcfResponse Fetch(FetchRequest request);15:
16: /// <summary>17: /// Update a business object.18: /// </summary>19: /// <param name="request">The request parameter object.</param>20: [OperationContract]21: [UseNetDataContract]22: WcfResponse Update(UpdateRequest request);23: }
24:
IDataPortalServer1: /// <summary>2: /// Interface implemented by server-side data portal3: /// components.4: /// </summary>5: public interface IDataPortalServer6: {7: /// <summary>8: /// Get an existing business object.9: /// </summary>10: /// <param name="objectType">Type of business object to retrieve.</param>11: /// <param name="criteria">Criteria object describing business object.</param>12: /// <param name="context">13: /// <see cref="Server.DataPortalContext" /> object passed to the server.14: /// </param>15: DataPortalResult Fetch(Type objectType, object criteria, DataPortalContext context);16:
17: /// <summary>18: /// Update a business object.19: /// </summary>20: /// <param name="obj">Business object to update.</param>21: /// <param name="context">22: /// <see cref="Server.DataPortalContext" /> object passed to the server.23: /// </param>24: DataPortalResult Update(object obj, DataPortalContext context);25: }
26:
事实上这两个接口都提供了 Fetch Update 都是操作数据门户的,不过一个是服务端,一个是客服端所以实现方式上也有点不同。
五、数据门户设计
DataPortalFacade 数据门户外观模式 也是实现IDataPortalServer接口,主要是权限的就是上下文的实现(ApplicationContext)。
DataPortalContext 数据上下文没有实现IDataPortalServer接口FinalDataPortal 最终的数据门户1: /// <summary>2: /// Implements the server-side DataPortal as discussed3: /// in Chapter 4.4: /// </summary>5: public class FinalDataPortal : IDataPortalServer6: {
7: /// <summary>8: /// Get an existing business object.9: /// </summary>10: /// <param name="objectType">Type of business object to retrieve.</param>11: /// <param name="criteria">Criteria object describing business object.</param>12: /// <param name="context">13: /// <see cref="Server.DataPortalContext" /> object passed to the server.14: /// </param>15: public DataPortalResult Fetch(Type objectType, object criteria, DataPortalContext context)16: {
17: // create an instance of the business object.18: var obj = Activator.CreateInstance(objectType, true);19:
20: MethodCaller.CallMethodIfImplemented(obj, "QueryBy", criteria);21:
22: // return the populated business object as a result23: return new DataPortalResult(obj);24: }
25:
26: /// <summary>27: /// Update a business object.28: /// </summary>29: /// <param name="obj">Business object to update.</param>30: /// <param name="context">31: /// <see cref="Server.DataPortalContext" /> object passed to the server.32: /// </param>33: public DataPortalResult Update(object obj, DataPortalContext context)34: {
35: // tell the business object to update itself36: var target = obj as Entity;37: if (target != null)38: {
39: target.SaveRoot();
40: }
41: else if (obj is Service)42: {
43: (obj as Service).ExecuteByDataPortal();44: }
45: else if (obj is EntityList)46: {
47: (obj as EntityList).SaveRootList();48: }
49: else50: {51: // this is an updatable collection or some other52: // non-BusinessBase type of object53: // tell the object to update itself54: MethodCaller.CallMethodIfImplemented(obj, "DataPortal_Update");55: }
56:
57: return new DataPortalResult(obj);58: }
59: }
60:
代码约定(实体列表类需要编写对应类型的 QueryBy 数据层方法完成数据访问逻辑)也就是每一个业务对象集合都的实现QueryBy方法。DataPortal_Update 在代码里没有找到不知道作者是这么实现的。纠结中........
六、业务对象知识补充
说起业务对象就不的不说一下业务对象的三要素了,如下来51CTO网站上的图片
■ 标识(identity),是唯一区别其他对象的标志;
■ 状态(state),描述对象所蕴含的信息;
■ 行为(behavior),对象所持有的、描述对象如何被使用的方法。
关于业务对象的理论这里有园里有一篇就将的很全面了,这里就不做补充了,大家有兴趣可以直接看作者的这篇:关于业务对象本质的思考(1)的文章。
以下是OEA的实体对象状态抽象:
以下是对象状态的实现 也就是Entity.Csla.cs 类的代码了:
1: /// <summary>2: /// 原-来′ Csla 中D的? BusinessBase 中D的?代ú码?,?都?移?动ˉ到?这a个?类à中D。£3: /// </summary>4: public abstract partial class Entity5: {6: #region PersistenceStatus7:
8: [NonSerialized]9: private PersistenceStatus _previousStatusBeforeDeleted = PersistenceStatus.Unchanged;10: private PersistenceStatus _status = PersistenceStatus.New;11:
12: public PersistenceStatus Status13: {
14: get { return this._status; }15: set16: {17: if (value != this._status)18: {
19: if (value == PersistenceStatus.Deleted)20: {
21: this._previousStatusBeforeDeleted = this._status;22: }
23:
24: this._status = value;25: }
26: }
27: }
28:
29: public bool IsNew30: {
31: get { return this.Status == PersistenceStatus.New; }32: }
33:
34: public bool IsDeleted35: {
36: get { return this.Status == PersistenceStatus.Deleted; }37: }
38:
39: public void MarkNew()40: {
41: this.Status = PersistenceStatus.New;42: }
43:
44: public void MarkDirty()45: {
46: if (this.Status != PersistenceStatus.New)47: {
48: this.Status = PersistenceStatus.Modified;49: }
50: }
51:
52: public virtual void MarkDeleted()53: {
54: this.Status = PersistenceStatus.Deleted;55: }
56:
57: public void RevertDeleted()58: {
59: this._status = this._previousStatusBeforeDeleted;60: }
61:
62: protected void MarkModified()63: {
64: if (this.Status == PersistenceStatus.Unchanged) { this.Status = PersistenceStatus.Modified; }65: }
66:
67: #endregion68:
69: #region IDirtyAware70:
88: public virtual bool IsSelfDirty89: {
90: get { return this.Status != PersistenceStatus.Unchanged; }91: }
92:
93: public virtual void MarkOld()94: {
95: this.Status = PersistenceStatus.Unchanged;96:
97: foreach (var field in this.GetNonDefaultPropertyValues())98: {
99: var value = field.Value as IDirtyAware;100: if (value != null && value.IsDirty) value.MarkOld();101: }
102: }
103:
104: #endregion105:
154: /// <summary>155: /// 这个事件不可以屏敝,否则状态会出问题156: /// </summary>157: /// <param name="e"></param>158: protected override void OnPropertyChanged(IManagedPropertyChangedEventArgs e)159: {
160: if (e.Source != ManagedPropertyChangedSource.FromPersistence)161: {
162: this.MarkModified();163: }
164:
165: base.OnPropertyChanged(e);166: }
167:
254: }255:
在那里使用了对象状态?
七、总结
这里主要是学到了什么是业务对象,WCF接口设计,服务实现,代理,.NET数据上下文的实现。
整体设计图纸:
不懂之处:DataPortal_Update没有找到,对象状态不知道在那里使用,还是说客服端传过来的?
技术不足之处,对数据上下文的理解。