Apply SOA Design Patterns with WCF (5) WCF Based ASP.NET DataSouce Control (基于WCF的数据源控件)

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

Original (原创) by Teddy’s Knowledge Base

Content (目录)

(1) WCF Configuration Centralization (WCF配置集中管理)

(2) WCF Automatic Deployment (WCF自动化部署)

(3) WCF Automatic Service Locating (WCF自动化服务定位)

(4) WCF Database Paging & Sorting (WCF数据库分页和排序)

(5) WCF Based ASP.NET DataSouce Control (基于WCF的数据源控件)

(6) 1 + 2 + 3 + 4 + 5 = ?

English Version

摘要

本文介绍如何实现一个基于WCF的ASP.NET数据源控件,从而使得跨WCF通信的数据库CRUD,尤其是复杂的分页排序更简单。

正文

我们需要基于WCF的ASP.NET数据源控件吗?

ASP.NET的数据源设计是的 ASP.NET页面上的数据绑定十分简单,但是.NET Framework到目前为止内置提供的DataSource控件,对WCF的支持都不是很方便。SqlDataSource和LinqDataSource暴露了太多SQL的细节,仅支持SQL Server数据库,并且完全不支持WCF;ObjectDataSOource则太通用了,以至于仅仅为了实现一个很简单的数据库分页排序功能也需要写很多代码。因此,如果我们基于WCF和ASP.NET来实现SOA,那么,一个基于WCF的ASP.NET数据源控件绝对值得去设计。

我们手头都有哪些武器了?
实现

首先,我们可以定义一个为所有的查询共享的WCF服务契约。下面的代码是IQueryService服务契约:

 1      [ServiceContract]
 2       public   interface  IQueryService
 3      {
 4          [OperationContract]
 5          DataTable Select(Criteria criteria);
 6          [OperationContract]
 7           int  SelectCount(Criteria criteria);
 8          [OperationContract]
 9           int  Update(Criteria criteria, DataTable modifiedTable, ConflictOption conflictDetection);
10      }

然后,我们可以为上面这个服务契约,定义一个基于查询对象的默认的实现:

 1       public   sealed   class  QueryService : IQueryService
 2      {
 3           public  DataTable Select(Criteria criteria)
 4          {
 5               if  (criteria  ==   null )
 6                   throw   new  ArgumentNullException( " criteria " );
 7 
 8               using  (var adapter  =   new  QueryCommandFactory(criteria).GetQueryDataAdapter())
 9              {
10                  var connection  =  adapter.SelectCommand.Connection;
11                   try
12                  {
13                       if  (connection.State  !=  ConnectionState.Open)
14                          connection.Open();
15                      var table  =   new  DataTable(criteria._tableName);
16                      adapter.Fill(table);
17                       return  table;
18                  }
19                   finally
20                  {
21                       if  (connection.State  !=  ConnectionState.Closed)
22                          connection.Close();
23                      connection.Dispose();
24                  }
25              }
26          }
27 
28           public   int  SelectCount(Criteria criteria)
29          {
30               if  (criteria  ==   null )
31                   throw   new  ArgumentNullException( " criteria " );
32 
33               using  (var cmd  =   new  QueryCommandFactory(criteria).GetCountCommand())
34              {
35                  var connection  =  cmd.Connection;
36                   try
37                  {
38                       if  (connection.State  !=  ConnectionState.Open)
39                          connection.Open();
40                       return  Convert.ToInt32(cmd.ExecuteScalar());
41                  }
42                   finally
43                  {
44                       if  (connection.State  !=  ConnectionState.Closed)
45                          connection.Close();
46                      connection.Dispose();
47                  }
48              }
49          }
50 
51           public   int  Update(Criteria criteria, DataTable modifiedTable, ConflictOption conflictDetection)
52          {
53               if  (criteria  ==   null )
54                   throw   new  ArgumentNullException( " criteria " );
55 
56               using  (var adapter  =   new  QueryCommandFactory(criteria).GetUpdatableQueryDataAdapter(conflictDetection))
57              {
58                  var connection  =  adapter.SelectCommand.Connection;
59                   try
60                  {
61                       if  (connection.State  !=  ConnectionState.Open)
62                          connection.Open();
63                       return  adapter.Update(modifiedTable);
64                  }
65                   finally
66                  {
67                       if  (connection.State  !=  ConnectionState.Closed)
68                          connection.Close();
69                      connection.Dispose();
70                  }
71              }
72          }
73      }

最后,只要通过能和第三方服务定位器整合的ServiceManager类,参见文章(3),如果我们能实现一个带一个Criteria属性作为查询的输入的数据源控件,我们就能很容易的基于能自动化的定位的IQueryService服务的实现CRUD。要实现一个自定义的自定义的数据源控件,可以参考.NET Framework中的SqlDataSource控件的实现,下面的代码是这个基于WCF的QueryDataSource控件的实现摘要:

  1       public   sealed   class  QueryDataSource : DataSourceControl
  2      {
  3           #region  Private Fields
  4 
  5           //
  6 
  7           #endregion
  8 
  9           #region  Protected Methods
 10 
 11           protected   override  DataSourceView GetView( string  viewName)
 12          {
 13               if  (_view  ==   null )
 14                  _view  =   new  QueryDataSourceView( this );
 15               return  _view;
 16          }
 17 
 18           protected   override   void  OnInit(System.EventArgs e)
 19          {
 20               base .OnInit(e);
 21 
 22               if  (HttpContext.Current  ==   null )
 23                   return ;
 24               if  ( ! UseLocalQueryService)
 25              {
 26                  _locator  =  ServiceManager.GetServiceLocator( typeof (IQueryService));
 27                  _service  =  _locator.GetService < IQueryService > ();
 28              }
 29               else
 30              {
 31                  var serviceType  =  Type.GetType(_defaultQueryServiceImplType);
 32                  _service  =  (IQueryService)Activator.CreateInstance(serviceType);
 33                   if  (_service  ==   null )
 34                       throw   new  FileLoadException( " Could not load assembly - NIntegrate.Query.Command.dll. " );
 35              }
 36          }
 37 
 38           #endregion
 39 
 40           #region  Public Properties
 41 
 42          [Category( " Data " ), DefaultValue( false ), Description( " When the value of this property equals true, it always using NIntegrate.Query.Command.QueryService class as QueryService insteads of trying to get the IQueryService implementation instance from ServiceManager class. " )]
 43           public   bool  UseLocalQueryService {  get set ; }
 44 
 45          [Category( " Data " ), Description( " Specify the criteria. " )]
 46           public  Criteria Criteria
 47          {
 48               internal   get
 49              {
 50                   if  (_criteria  ==   null   &&  EnableViewState
 51                       &&  ViewState[ " Criteria " !=   null )
 52                  {
 53                      _criteria  =  QueryHelper.CriteriaDeserialize(
 54                          ( string )ViewState[ " Criteria " ]);
 55                  }
 56 
 57                   return  _criteria;
 58              }
 59               set
 60              {
 61                   if  (value  ==   null )
 62                       return ;
 63 
 64                  _criteria  =  value;
 65                   if  (EnableViewState)
 66                      ViewState[ " Criteria " =  QueryHelper.CriteriaSerialize(value.ToBaseCriteria());
 67              }
 68          }
 69 
 70           //
 71 
 72           #endregion
 73 
 74           #region  Events
 75 
 76           //
 77 
 78           #endregion
 79 
 80           #region  Dispose()
 81 
 82           public   override   void  Dispose()
 83          {
 84              Dispose( true );
 85              GC.SuppressFinalize( this );
 86          }
 87 
 88           private   bool  disposed;
 89 
 90           private   void  Dispose( bool  disposing)
 91          {
 92               if  (disposed)  return ;
 93               if  (disposing)
 94              {
 95                  var dispose  =  _service  as  IDisposable;
 96                   if  (dispose  !=   null )
 97                      dispose.Dispose();
 98                   if  (_locator  !=   null )
 99                      _locator.Dispose();
100              }
101 
102              disposed  =   true ;
103          }
104 
105           ~ QueryDataSource()
106          {
107              Dispose( false );
108          }
109 
110           #endregion
111      }
112 
113       internal   sealed   class  QueryDataSourceView : DataSourceView
114          {
115               #region  Private Membes
116 
117               //
118 
119               #endregion
120 
121               #region  Constructors
122 
123               internal  QueryDataSourceView(QueryDataSource owner)
124                  :  base (owner,  " Default " )
125              {
126                   if  (owner  ==   null )
127                       throw   new  ArgumentNullException( " owner " );
128 
129                  _owner  =  owner;
130              }
131 
132               #endregion
133 
134               #region  Public Properties
135 
136               public   override   bool  CanInsert
137              {
138                   get
139                  {
140                       return   true ;
141                  }
142              }
143 
144               public   override   bool  CanUpdate
145              {
146                   get
147                  {
148                       return   true ;
149                  }
150              }
151 
152               public   override   bool  CanDelete
153              {
154                   get
155                  {
156                       return   true ;
157                  }
158              }
159 
160               public   override   bool  CanRetrieveTotalRowCount
161              {
162                   get
163                  {
164                       return   true ;
165                  }
166              }
167 
168               public   override   bool  CanPage
169              {
170                   get
171                  {
172                       return   true ;
173                  }
174              }
175 
176               public   override   bool  CanSort
177              {
178                   get
179                  {
180                       return   true ;
181                  }
182              }
183 
184               #endregion
185 
186               #region  Protected Methods
187 
188               protected   override   int  ExecuteInsert(IDictionary values)
189              {
190                   //
191 
192                  var table  =  _owner._service.Select(criteria);
193                  var row  =  table.NewRow();
194                  var en  =  values.GetEnumerator();
195                   while  (en.MoveNext())
196                  {
197                       if  (table.Columns.Contains(en.Key.ToString()))
198                          row[en.Key.ToString()]  =  TransformType(table.Columns[en.Key.ToString()].DataType, en.Value);
199                  }
200                  table.Rows.Add(row);
201 
202                  var conflictDetection  =  ConflictOption.OverwriteChanges;
203                   if  (_owner.ConflictDetection  ==  ConflictOptions.CompareAllValues)
204                  {
205                      conflictDetection  =  ConflictOption.CompareAllSearchableValues;
206                  }
207 
208                  var affectedRows  =  _owner._service.Update(_owner.Criteria, table, conflictDetection);
209 
210                   //
211              }
212 
213               protected   override   int  ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues)
214              {
215                  //
216 
217                  var table  =  _owner._service.Select(criteria);
218                   if  (table  ==   null   ||  table.Rows.Count  ==   0 )
219                       throw   new  DataException( " No row is matching specified key values. " );
220                   if  (table.Rows.Count  >   1 )
221                       throw   new  DataException( " More than one rows are matching specified key values, please check the key columns setting. " );
222                  var row  =  table.Rows[ 0 ];
223                  var conflictDetection  =  ConflictOption.OverwriteChanges;
224                   if  (_owner.ConflictDetection  ==  ConflictOptions.CompareAllValues)
225                  {
226                      DetectDataRowConflicts(oldValues, row);
227                      conflictDetection  =  ConflictOption.CompareAllSearchableValues;
228                  }
229 
230                  var en  =  values.GetEnumerator();
231                   while  (en.MoveNext())
232                  {
233                       if  (table.Columns.Contains(en.Key.ToString()))
234                          row[en.Key.ToString()]  =  TransformType(table.Columns[en.Key.ToString()].DataType, en.Value);
235                  }
236 
237                  var affectedRows  =  _owner._service.Update(_owner.Criteria, table, conflictDetection);
238 
239                   //
240              }
241 
242               protected   override   int  ExecuteDelete(IDictionary keys, IDictionary oldValues)
243              {
244                   //
245 
246                  var table  =  _owner._service.Select(criteria);
247                   if  (table  ==   null   ||  table.Rows.Count  ==   0 )
248                       throw   new  DataException( " No row is matching specified key values. " );
249                   if  (table.Rows.Count  >   1 )
250                       throw   new  DataException( " More than one rows are matching specified key values, please check the key columns setting. " );
251 
252                  var row  =  table.Rows[ 0 ];
253                  var conflictDetection  =  ConflictOption.OverwriteChanges;
254                   if  (_owner.ConflictDetection  ==  ConflictOptions.CompareAllValues)
255                  {
256                      DetectDataRowConflicts(oldValues, row);
257                      conflictDetection  =  ConflictOption.CompareAllSearchableValues;
258                  }
259 
260                  row.Delete();
261 
262                  var affectedRows  =  _owner._service.Update(_owner.Criteria, table, conflictDetection);
263                   //
264              }
265 
266               protected   override  IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
267              {
268                   //
269 
270                   if  (arguments  !=   null   &&  arguments  !=  DataSourceSelectArguments.Empty)
271                  {
272                       // adjust criteria according to arguments
273                       if  (arguments.RetrieveTotalRowCount)
274                      {
275                          arguments.TotalRowCount  =  _owner._service.SelectCount(criteria);
276                          _owner.LastTotalCount  =  arguments.TotalRowCount;
277                      }
278                       if  (arguments.MaximumRows  >   0 )
279                          criteria.MaxResults(arguments.MaximumRows);
280                       if  (arguments.StartRowIndex  >   0 )
281                          criteria.SkipResults(arguments.StartRowIndex);
282                       if  ( ! string .IsNullOrEmpty(arguments.SortExpression))
283                      {
284                           if  (_owner.AlwaysAppendDefaultSortBysWhenSorting)
285                              InsertSortExpressinAtTopOfSortBys(arguments.SortExpression, criteria);
286                           else
287                          {
288                              criteria._sortBys.Clear();
289                              AppendSortExpression(criteria, arguments.SortExpression);
290                          }
291                      }
292                  }
293 
294                   return   new  DataView(_owner._service.Select(criteria));
295              }
296 
297               #endregion
298          }

参考

(1) SOA Design Pattern Catalog: http://www.soapatterns.org/

//我是结尾符,待续…

Tag标签: nintegrate soa
2026年最新深圳市公交、地铁线路及站点矢量数据.zip 数据格式:shp 数据坐标:GCJ02 数据更新时间:2026年9月 公交线路来源:8684网站 https://8684.com.cn/ 站点数据来源:高德API接口 数据打开方式:QGIS或Arcgis 站点数据字段:名称、序号、对应线路、几何信息 线路数据字段:名称、类型、起点、终点、开始时间、结束时间、起步价、全价、长度、公司、几何信息 立即下载

相关推荐

设备管理基于OEE数据的闲置富余设备识别系统:制造业设备利用率监控与预警方案设计

内容概要:本文档为鹏鼎EES项目第二阶段关于设备闲置与富余识别的需求设计方案,旨在通过自动化方式识别低利用率设备,减少资产浪费。系统基于OEE系统提供的设备近6个月时间稼动率数据,设定“闲置”(连续6个月稼动率为0%)和“富余”(6个月平均稼动率≤30%)的判断标准,每周一自动执行识别任务并生成记录。支持在系统中查看识别结果列表、筛选导出数据、发起闲置申请及删除记录(管理员权限)。同时,系统通过鼎加机器人按设备闲置/富余持续时长(7天、30天、90天、180天)逐级向上推送预警消息至维护人员、厂长、处长、经管等层级,推动问题处理。此外,若设备被判定为闲置但未提交闲置申请,系统将向维护人员和设备课长发送D+提醒。; 适合人群:系统设计人员、开发人员、测试人员、设备管理人员及项目实施相关人员;尤其适用于熟悉OEE系统、设备管理流程及企业信息化系统的专业人员;; 使用场景及目标:① 实现设备利用率的动态监控与闲置风险预警;② 支持企业优化设备资源配置,降低资产闲置成本;③ 推动设备闲置处理流程自动化与责任到人机制建立;④ 为后续设备处置、调配、报废等决策提供数据支撑;; 阅读建议:本文档为研发与实施阶段的核心指导文件,涉及系统逻辑、数据来源、权限控制与集成接口等关键内容,建议结合OEE数据对接情况、企业组织架构与设备管理流程协同研读,并关注阈值配置、提醒机制与状态联动等可配置项的实际业务适配性。

理解Design Pattern

<br />本文的目的是以最精炼的语言,理解什么是Design Pattern,为什么需要Design Pattern,和关于Design Pattern的争论。什么是Design Pattern?<br />从字面上理解,Pattern,指的是解决常见问题的可重用的方案。Design,则限定了,这里的Pattern是为软件的设计服务的。因此,所谓Design Pattern,指的是解决常见的软件设计问题的可重用的方案,或者说解决方案的模板。<br /> <br />两点需要特别注意的是:算法不是Desig

Teddy's Knowledge Base 4794

RK3568 生产写SN实战:基于 RKDevInfoWriteTool 的序列号烧录方案

RK3568 生产写SN实战:基于 RKDevInfoWriteTool 的序列号烧录方案

C#2.0 - Object Pool 简单实现

本文介绍本人的一个简单Object Pool实现。什么是Object Pool呢?大家可能都知道什么是数据库连接池,他能极大避免不需要的对象销毁和初始化开销。本文实现的对象池是一个通用的可用于任何有实例化接口的对象的池。默认的对象的实例化接口是new,文中也演示了如果您的对象需要从一个Factory构造,或当你的对象是用Emit生成时,如何简单继承ObjectPool类,实现特殊的对象的

Teddy's Knowledge Base 1365

Enterprise WCF - Integrate WCF Configuration into Your Enterprise Framework

Background Lots of people declares they are using WCF in their “enterprise” distributed software development together with other SOA technologies, and even calls their software architecture WCF b

Teddy's Knowledge Base 1213

讨论:WCF可序列化的ActiveRecord实现有意义吗?

<br />本文的目的,是探讨WCF可序列化的ActiveRecord实现是否有意义。ActiveRecord模式简介<br />ActiveRecord模式是Martin Fowler的企业应用架构模式中提到的一种数据访问模式,如下图:<br /><br />Person类包含lastName, firstName等这些属性,一个Person类的实例,对应数据库的一个Person表或视图的一条记录,每一个属性,对应数据库记录的一个字段;同时,Person类包含CRUD方法,封装了对数据库的操作;Perso

Teddy's Knowledge Base 1201

理解单元测试(Unit Testing)

<br />本文的目的是以最精炼的语言,正解什么是单元测试,为什么要单元测试,和如何进行单元测试。 什么是单元测试(Unit Testing)?<br />测试(Testing)这个词很容易理解,那么什么是单元(Unit)呢?一个单元指的是应用程序中可测试的最小的一组源代码。一组源代码可测试,一般要求其有明确的输入和输出。因此,一般来讲,源代码中包含明确的输入和输出的每一个方法被认为是一个可测试的单元。注意,这里指的输出,并不局限于方法的返回值或对输入参数的改变,而包括了方法的执行过程中,改变的任何数据。为

Teddy's Knowledge Base 1196

提前发布4/25上海俱乐部活动演讲“集中化WCF服务配置管理”示例程序

承蒙4/25上海俱乐部活动组织团队邀请,本人有幸将在本次活动中和大家分享一下“集中化WCF服务配置管理”方面的一些探索。为了让大家对活动当天我讲的话题提前有一个更直观的了解,在这里提前介绍一下到时我会演示的示例程序。 本示例程序具体演示如下内容: 1) 基于SQL Server数据库的集中化WCF服务配置管理; 2) 集中化ConnectingString管理; 3) 集中化App

Teddy's Knowledge Base 1002

来一点反射,再来一点Emit —— 极度简化Entity!

在前一篇文章《没有ORM或代码生成数据就不能持久化了? - 用范型技术代替代码生成!》中,Teddy尝试运用泛型极大简化了一个轻量级持久化框架对代码生成的依赖,并且为了保证性能,整个持久化组件没有使用反射。在本文中,Teddy将在保证性能的基础上,加一点反射和加一点Emit,从而进一步简化Entity的定义和使用,当然也就进一步降低了组件对传统代码生成的依赖。读者可以对比前文阅读本文,看

Teddy's Knowledge Base 959

Web Application Architectures: Simple 2 Layer, Standard 3 Layer and Distributed 3 Layer

Abstract:This article discusses three common web application architectures: Simple 2 Layer, Standard 3 Layer and Distributed 3 Layer. For each architecture, it gives an ASP.NET 2.0 sample imple

Teddy's Knowledge Base 921

ORM中的继承关系映射全解——单表继承体系、一实体一具体表、一实体一扩展表、接口映射

实体继承是基于OO和关系型数据库软件系统设计中的一个重要主题。本文通过基于NBear的实例解析ORM中的实体继承体系映射的方方面面。本文涉及的内容包括:1. 单表继承体系2. 一实体一具体表3. 一实体一扩展表4. 接口实现映射vs基类继承映射1. 单表继承体系所谓单表继承体系就是用一张数据库表存储整个继承体系中的所有实体的数据。单表继承体系适合那种继承体系中实体数目相对较少,总记录

Teddy's Knowledge Base 897

发布NBearLite中文版完全参考手册 + NBearLite 10分钟入门教程 + NBearLite v1.0.0.7 beta

NBearLite是一个.NET 2.0开源(BSD协议)通用数据访问组件,支持SQL Server、Oracle、MS Access,MySQL,SQLite,PostgreSQL等多种数据库的透明切换(改变数据库类型时无需修改具体的查询代码)。NBearLite实现了一套类似LINQ的强类型数据库查询语法(支持从简单的CRUD查询到GroupBy、InnerJoin,Paging,S

Teddy's Knowledge Base 891

用自定义KeyValueCollection类代替Dictionary/Hastable,改善简化后的Entity性能

在上一篇《来一点反射,再来一点Emit —— 极度简化Entity!》中,Teddy运用反射和Emit极度简化了Entity的定义方式。本文将在上文的基础上,用自定义KeyValueCollection类代替原来的Dictionary类承载Entity的数据,从而改善Entity的读写性能,并保持Dictionary的方便的使用接口。为什么Dictionary的性能不好?当然,这里说的性

Teddy's Knowledge Base 873

ASP.Net 2.0 - 使用Ilungasoft Framework加速Web开发

Ilungasoft Framework是Teddy正在开发中的Web快速开发框架,该框架基于.Net 2.0,设计的出发点就是易用性,快速开发,兼顾性能。同时,Ilungasoft Framework也充分利用了许多.Net 2.0中的新特性以简化框架的易用性。本文将以一个Sample Web应用的分析为基础,介绍Ilungasoft Framework的使用。在本文中,您将看到,Il

Teddy's Knowledge Base 838

正解TDD

<br />本文的目的是以最精炼的语言,正解什么是TDD,为什么要TDD,和TDD的难点。 <br />什么是TDD? 简单的说,TDD = 测试先行(TFD, Test First Development) + 重构(Refactoring) + 回归测试(Regression Test)。<br /><br /><br />如果要实现某个功能,TDD要求在初步定义完这个功能的外部接口之后,先根据这个功能的用例写测试代码(黑盒测试),测试代码检验的是这个功能的外部接口的使用场景,而非具体的实现细节。然后,

Teddy's Knowledge Base 836

Apply SOA Design Patterns with WCF (2) WCF Automatic Deployment (自动化部署)

Original (原创) by Teddy’s Knowledge Base Content (目录) (1) WCF Configuration Centralization (WCF配置集中管理) (2) WCF Automatic Deployment (WCF自动化部署) (3) WCF Automatic Service Locating (WCF自动化服务定位) (4)

Teddy's Knowledge Base 827

NVMe CMB与PMR深度解析:控制器内存缓冲区、持久化内存区域、队列零拷贝放置与内核驱动全栈剖析

本文深度解析NVMe 1.4+引入的CMB(Controller Memory Buffer)与PMR(Persistent Memory Region)两大高级特性,涵盖CMB能力寄存器定义、SQ/CQ队列放置策略、SGL数据段驻留机制、PMR持久化级别(0-3级)与写冲刷流程、PCIe BAR空间映射原理、Linux内核nvme-core的CMB队列分配与I/O路径优化,结合fio/spdk实测数据量化CMB对随机IOPS与尾延迟的提升幅度,以及PMR在掉电保护场景下的持久化开销。

专注分享DPU、RDMA智能网卡/SSD(固态硬盘)产品测试与优化 508

地方科技部门如何借助数智化平台提升科技项目评审效率?.docx

科易网基于40亿+科创知识图谱数据库,深度探索AI技术在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,研究科技创新领域的AI+数智化解决方案,推动科技创新与产业创新智能化发展。

上一篇: Apply SOA Design Patterns with WCF (4) WCF Database Paging & Sorting (数据库端分页和排序)
下一篇: 提前发布4/25上海俱乐部活动演讲&ldquo;集中化WCF服务配置管理&rdquo;示例程序
teddyma
博客等级 码龄25年 5粉丝 187原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值