Go to my github

WCF开发日志 -- OEA里面的WCF设计

一、摘要

场景: 

      最近公司的项目中用到了WCF 就想着分析OEA里的WCF设计,吸取一下高手的经验 。

类图:

       今天在这里主要是分析OEA里的WCF设计(也叫分布式业务对象数据门户)。先看一下OEA类库

       clipboard

二、本文大纲

       a、摘要 。

       b、本文大纲 。

       c、WCF接口设计。

       d、WCF 交互设计 。

       e、数据门户设计 。

       f、业务对象知识补充 。

三、WCF接口设计

关于WCF接口的相关知识可以看以前写WCF 开发日志 -- WCF契约设计

 clipboard[5]

消息契约(MessageContract)  在 WCF 开发日志 -- WCF契约设计 有提到一点,在网上和MSDN上有更详细的说明了,这里就不详细描述了。

WcfResponse , FetchRequest , UpdateRequest 
上面类图对应有三个文件,其实这三个文件的内容都差不多,只是职责不同。
clipboard[7]

 

四、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 : IWcfPortal
 9:    {
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:            try
19:            {
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:            try
38:            {
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 portal
  3:      /// calls to a remote application server by using WCF.
  4:      /// </summary>
  5:      public class WcfProxy : OEA.DataPortalClient.IDataPortalProxy,IDataPortalServer
  6:      {
  7:          #region IDataPortalProxy Members
  8:   
  9:          /// <summary>
 10:          /// Gets a value indicating whether the data portal
 11:          /// is hosted on a remote server.
 12:          /// </summary>
 13:          public bool IsServerRemote
 14:          {
 15:              get { return true; }
 16:          }
 17:   
 18:          #endregion
 19:  
 20:          #region IDataPortalServer Members
 21:   
 22:          private string _endPoint = "WcfDataPortal";
 23:   
 24:          /// <summary>
 25:          /// Gets or sets the WCF endpoint used
 26:          /// to contact the server.
 27:          /// </summary>
 28:          /// <remarks>
 29:          /// The default value is WcfDataPortal.
 30:          /// </remarks>
 31:          protected string EndPoint
 32:          {
 33:              get
 34:              {
 35:                  return _endPoint;
 36:              }
 37:              set
 38:              {
 39:                  _endPoint = value;
 40:              }
 41:          }
 42:   
 43:          /// <summary>
 44:          /// Returns an instance of the channel factory
 45:          /// used by GetProxy() to create the WCF proxy
 46:          /// 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 for
 55:          /// communication with the data portal
 56:          /// 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 an
 68:          /// 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:              try
 81:              {
 82:                  response =
 83:                         svr.Fetch(new FetchRequest(objectType, criteria, context));
 84:                  if (cf != null)
 85:                      cf.Close();
 86:              }
 87:              catch
 88:              {
 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 a
101:          /// 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:              try
113:              {
114:                  response =
115:                        svr.Update(new UpdateRequest(obj, context));
116:                  if (cf != null)
117:                      cf.Close();
118:              }
119:              catch
120:              {
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:          #endregion
133:      }
134:   

 

IWcfPortal接口 和 IDataPortalServer接口 介绍  其实这两个接口都提供了 Fetch Update
 1:  /// <summary>
 2:     /// Defines the service contract for the WCF data
 3:     /// portal.
 4:     /// </summary>
 5:     [ServiceContract(Namespace = "http://ws.lhotka.net/WcfDataPortal")]
 6:     public interface IWcfPortal
 7:     {
 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:   
IDataPortalServer
 1:  /// <summary>
 2:     /// Interface implemented by server-side data portal
 3:     /// components.
 4:     /// </summary>
 5:     public interface IDataPortalServer
 6:     {
 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 discussed
 3:  /// in Chapter 4.
 4:  /// </summary>
 5:  public class FinalDataPortal : IDataPortalServer
 6:  {
 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 result
23:          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 itself
36:          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:          else
50:          {
51:              // this is an updatable collection or some other
52:              // non-BusinessBase type of object
53:              // tell the object to update itself
54:              MethodCaller.CallMethodIfImplemented(obj, "DataPortal_Update");
55:          }
56:   
57:          return new DataPortalResult(obj);
58:      }
59:  }
60:   

 

代码约定(实体列表类需要编写对应类型的 QueryBy 数据层方法完成数据访问逻辑)也就是每一个业务对象集合都的实现QueryBy方法。DataPortal_Update 在代码里没有找到不知道作者是这么实现的。纠结中........

 

六、业务对象知识补充

说起业务对象就不的不说一下业务对象的三要素了,如下来51CTO网站上的图片

113119383

■ 标识(identity),是唯一区别其他对象的标志;

■ 状态(state),描述对象所蕴含的信息;

■ 行为(behavior),对象所持有的、描述对象如何被使用的方法。

关于业务对象的理论这里有园里有一篇就将的很全面了,这里就不做补充了,大家有兴趣可以直接看作者的这篇:关于业务对象本质的思考(1)的文章。

以下是OEA的实体对象状态抽象:

clipboard[9]

以下是对象状态的实现 也就是Entity.Csla.cs  类的代码了:

  1:  /// <summary>
  2:   /// 原-来′ Csla 中D的? BusinessBase 中D的?代ú码?,?都?移?动ˉ到?这a个?类à中D。£
  3:   /// </summary>
  4:   public abstract partial class Entity 
  5:   {
  6:       #region PersistenceStatus
  7:   
  8:       [NonSerialized]
  9:       private PersistenceStatus _previousStatusBeforeDeleted = PersistenceStatus.Unchanged;
 10:       private PersistenceStatus _status = PersistenceStatus.New;
 11:   
 12:       public PersistenceStatus Status
 13:       {
 14:           get { return this._status; }
 15:           set
 16:           {
 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 IsNew
 30:       {
 31:           get { return this.Status == PersistenceStatus.New; }
 32:       }
 33:   
 34:       public bool IsDeleted
 35:       {
 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:       #endregion
 68:  
 69:       #region IDirtyAware
 70:   
 88:       public virtual bool IsSelfDirty
 89:       {
 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:       #endregion
105: 
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数据上下文的实现。

整体设计图纸:

image

不懂之处:DataPortal_Update没有找到,对象状态不知道在那里使用,还是说客服端传过来的?

技术不足之处,对数据上下文的理解。

posted @ 2012-09-15 01:46  峡谷少爷  阅读(848)  评论(0编辑  收藏  举报