基于C#+Thrift操作HBase实践

简介:

在基于HBase数据库的开发中,对应Java语言来说,可以直接使用HBase的原生API来操作HBase表数据,当然你要是不嫌麻烦可以使用Thrift客户端Java API,这里有我曾经使用过的 HBase Thrift客户端Java API实践,可以参考。对于具有其他编程语言背景的开发人员,为了获取HBase带来的好处,那么就可以选择使用HBase Thrift客户端对应编程语言的API,来实现与HBase的交互。
这里,我们使用C#客户端来操作HBase。HBase的Thrift接口的定义,可以通过链接http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/resources/org/apache/hadoop/hbase/thrift/Hbase.thrift?view=markup看到,我们需要安装Thrift编译器,才能生成HBase跨语言的API,这里,我使用的版本是0.9.0。需要注意的是,一定要保证,安装了某个版本Thrift的Thrift编译器,在导入对应语言库的时候,版本一定要统一,否则就会出现各种各样的问题,因为不同Thrift版本,对应编程语言的库API可能有变化。
首先,下载上面链接的内容,保存为Hbase.thrift。
然后,执行如下命令,生成C#编程语言的HBase Thrift客户端API:

1 [hadoop@master hbase]$ thrift --gen csharp Hbase.thrift
2 [hadoop@master hbase]$ ls
3 gen-csharp

这里,我们基于C#语言,使用HBase 的Thrift 客户端API访问HBase表。事实上,如果使用Java来实现对HBase表的操作,最好是使用HBase的原生API,无论从性能还是便利性方面,都会提供更好的体验。使用Thrift API访问,实际也是在HBase API之上进行了一层封装,可能初次使用Thrift API感觉很别扭,有时候还要参考Thrift服务端的实现代码。
准备工作如下:

    1. 下载Thrift软件包,解压缩后,拷贝thrift-0.9.0/lib/java/src下面的代码到工作区(开发工具中)
    2. 将上面生成的gen-csharp目录中代码拷贝到工作区
    3. 保证HBase集群正常运行,接着启动HBase的Thrift服务,执行如下命令:
1 bin/hbase thrift -b master -p 9090 start

上面,HBase的Thrift服务端口为9090,下面通过Thrift API访问的时候,需要用到,而不是HBase的服务端口(默认60000)。
接着,实现一个简单的例子,访问Hbase表。
首先,我们通过HBase Shell创建一个表:

1 create 'test_info', 'info'

表名为test_info,列簇名称为info。
然后,我们开始基于上面生成的Thrift代码来实现对HBase表的操作。
这里,我们实际上是对HBase Thrift客户端Java API实践中的Java代码进行了翻译,改写成C#语言的相关操作。我们在客户端,进行了一层抽象,更加便于传递各种参数,抽象类为AbstractHBaseThriftService,对应的命名空间为HbaseThrift.HBase.Thrift,该类实现代码如下所示:

01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Text;
05 using System.Threading.Tasks;
06
07 using Thrift.Transport;
08 using Thrift.Protocol;
09
10 namespace HbaseThrift.HBase.Thrift
11 {
12 public abstract class AbstractHBaseThriftService
13 {
14 protected static readonly string CHARSET = "UTF-8";
15 private string host = "localhost";
16 private int port = 9090;
17 private readonly TTransport transport;
18 protected readonly Hbase.Client client;
19
20 public AbstractHBaseThriftService() : this("localhost", 9090)
21 {
22
23 }
24
25 public AbstractHBaseThriftService(string host, int port)
26 {
27 this.host = host;
28 this.port = port;
29 transport = new TSocket(host, port);
30 TProtocol protocol = new TBinaryProtocol(transport, true, true);
31 client = new Hbase.Client(protocol);
32 }
33
34 public void Open() {
35 if (transport != null)
36 {
37 transport.Open();
38 }
39 }
40
41 public void Close()
42 {
43 if (transport != null)
44 {
45 transport.Close();
46 }
47 }
48
49 public abstract List<string> GetTables();
50
51 public abstract void Update(string table, string rowKey, bool writeToWal,
52 string fieldName, string fieldValue, Dictionary<string, string> attributes);
53 public abstract void Update(string table, string rowKey, bool writeToWal,
54 Dictionary<string, string> fieldNameValues, Dictionary<string, string> attributes);
55
56 public abstract void DeleteCell(string table, string rowKey, bool writeToWal,
57 string column, Dictionary<string, string> attributes);
58 public abstract void DeleteCells(string table, string rowKey, bool writeToWal,
59 List<string> columns, Dictionary<string, string> attributes);
60
61 public abstract void DeleteRow(string table, string rowKey,
62 Dictionary<string, string> attributes);
63
64 public abstract int ScannerOpen(string table, string startRow, List<string> columns,
65 Dictionary<string, string> attributes);
66 public abstract int ScannerOpen(string table, string startRow, string stopRow, List<string> columns,
67 Dictionary<string, string> attributes);
68 public abstract int ScannerOpenWithPrefix(string table, string startAndPrefix,
69 List<string> columns, Dictionary<string, string> attributes);
70 public abstract int ScannerOpenTs(string table, string startRow,
71 List<string> columns, long timestamp, Dictionary<string, string> attributes);
72 public abstract int ScannerOpenTs(string table, string startRow, stringstopRow,
73 List<string> columns, long timestamp, Dictionary<string, string> attributes);
74
75 public abstract List<TRowResult> ScannerGetList(int id, int nbRows);
76 public abstract List<TRowResult> ScannerGet(int id);
77
78 public abstract List<TRowResult> GetRow(string table, string row,
79 Dictionary<string, string> attributes);
80 public abstract List<TRowResult> GetRows(string table,
81 List<string> rows, Dictionary<string, string> attributes);
82 public abstract List<TRowResult> GetRowsWithColumns(string table,
83 List<string> rows, List<string> columns, Dictionary<string, string> attributes);
84
85 public abstract void ScannerClose(int id);
86
87 /**
88 * Iterate result rows(just for test purpose)
89 * @param result
90 */
91 public abstract void IterateResults(TRowResult result);
92
93 }
94 }

这里,简单叙述一下,我们提供的客户端API的基本功能:

  • 建立到Thrift服务的连接:Open()
  • 获取到HBase中的所有表名:GetTables()
  • 更新HBase表记录:Update()
  • 删除HBase表中一行的记录的数据(cell):DeleteCell()和DeleCells()
  • 删除HBase表中一行记录:deleteRow()
  • 打开一个Scanner,返回id:ScannerOpen()、ScannerOpenWithPrefix()和ScannerOpenTs();然后用返回的id迭代记录:ScannerGetList()和ScannerGet()
  • 获取一行记录结果:GetRow()、GetRows()和GetRowsWithColumns()
  • 关闭一个Scanner:ScannerClose()
  • 迭代结果,用于调试:IterateResults()

比如,我们想要实现分页的逻辑,可能和传统的关系型数据库操作有些不同。基于HBase表的实现是,首先打开一个Scanner实例(例如调用ScannerOpen()),返回一个id,然后再使用该id,调用ScannerGetList()方法(可以指定每次返回几条记录的变量nbRows的值),返回一个记录列表,反复调用该ScannerGetList()方法,直到此次没有结果返回为止。后面会通过测试用例来实际体会。
现在,我们基于上抽象出来的客户端操作接口,给出一个基本的实现,代码如下所示:

001 using System;
002 using System.Collections.Generic;
003 using System.Linq;
004 using System.Text;
005 using System.Threading.Tasks;
006
007 namespace HbaseThrift.HBase.Thrift
008 {
009 class HBaseThriftClient : AbstractHBaseThriftService
010 {
011 public HBaseThriftClient() : this("localhost", 9090)
012 {
013
014 }
015
016 public HBaseThriftClient(string host, int port) : base(host, port)
017 {
018
019 }
020
021 public override List<string> GetTables()
022 {
023 List<byte[]> tables = client.getTableNames();
024 List<String> list = new List<String>();
025 foreach(byte[] table in tables)
026 {
027 list.Add(Decode(table));
028 }
029 return list;
030 }
031
032 public override void Update(string table, string rowKey, bool writeToWal,string fieldName, string fieldValue, Dictionary<string, string> attributes)
033 {
034 byte[] tableName = Encode(table);
035 byte[] row = Encode(rowKey);
036 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
037 List<Mutation> mutations = new List<Mutation>();
038 Mutation mutation = new Mutation();
039 mutation.IsDelete = false;
040 mutation.WriteToWAL = writeToWal;
041 mutation.Column = Encode(fieldName);
042 mutation.Value = Encode(fieldValue);
043 mutations.Add(mutation);
044 client.mutateRow(tableName, row, mutations, encodedAttributes);
045 }
046
047 public override void Update(string table, string rowKey, bool writeToWal, Dictionary<string, string> fieldNameValues, Dictionary<string, string> attributes)
048 {
049 byte[] tableName = Encode(table);
050 byte[] row = Encode(rowKey);
051 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
052 List<Mutation> mutations = new List<Mutation>();
053 foreach (KeyValuePair<String, String> pair in fieldNameValues)
054 {
055 Mutation mutation = new Mutation();
056 mutation.IsDelete = false;
057 mutation.WriteToWAL = writeToWal;
058 mutation.Column = Encode(pair.Key);
059 mutation.Value = Encode(pair.Value);
060 mutations.Add(mutation);
061 }
062 client.mutateRow(tableName, row, mutations, encodedAttributes);
063 }
064
065 public override void DeleteCell(string table, string rowKey, bool writeToWal,string column, Dictionary<string, string> attributes)
066 {
067 byte[] tableName = Encode(table);
068 byte[] row = Encode(rowKey);
069 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
070 List<Mutation> mutations = new List<Mutation>();
071 Mutation mutation = new Mutation();
072 mutation.IsDelete = true;
073 mutation.WriteToWAL = writeToWal;
074 mutation.Column = Encode(column);
075 mutations.Add(mutation);
076 client.mutateRow(tableName, row, mutations, encodedAttributes);
077 }
078
079 public override void DeleteCells(string table, string rowKey, bool writeToWal, List<string> columns, Dictionary<string, string> attributes)
080 {
081 byte[] tableName = Encode(table);
082 byte[] row = Encode(rowKey);
083 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
084 List<Mutation> mutations = new List<Mutation>();
085 foreach (string column in columns)
086 {
087 Mutation mutation = new Mutation();
088 mutation.IsDelete = true;
089 mutation.WriteToWAL = writeToWal;
090 mutation.Column = Encode(column);
091 mutations.Add(mutation);
092 }
093 client.mutateRow(tableName, row, mutations, encodedAttributes);
094 }
095
096 public override void DeleteRow(string table, string rowKey, Dictionary<string,string> attributes)
097 {
098 byte[] tableName = Encode(table);
099 byte[] row = Encode(rowKey);
100 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
101 client.deleteAllRow(tableName, row, encodedAttributes);
102 }
103
104 public override int ScannerOpen(string table, string startRow, List<string> columns, Dictionary<string, string> attributes)
105 {
106 byte[] tableName = Encode(table);
107 byte[] start = Encode(startRow);
108 List<byte[]> encodedColumns = EncodeStringList(columns);
109 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
110 return client.scannerOpen(tableName, start, encodedColumns, encodedAttributes);
111 }
112
113 public override int ScannerOpen(string table, string startRow, string stopRow, List<string> columns, Dictionary<string, string> attributes)
114 {
115 byte[] tableName = Encode(table);
116 byte[] start = Encode(startRow);
117 byte[] stop = Encode(stopRow);
118 List<byte[]> encodedColumns = EncodeStringList(columns);
119 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
120 return client.scannerOpenWithStop(tableName, start, stop, encodedColumns, encodedAttributes);
121 }
122
123 public override int ScannerOpenWithPrefix(string table, string startAndPrefix, List<string> columns, Dictionary<string, string> attributes)
124 {
125 byte[] tableName = Encode(table);
126 byte[] prefix = Encode(startAndPrefix);
127 List<byte[]> encodedColumns = EncodeStringList(columns);
128 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
129 return client.scannerOpenWithPrefix(tableName, prefix, encodedColumns, encodedAttributes);
130 }
131
132 public override int ScannerOpenTs(string table, string startRow, List<string> columns, long timestamp, Dictionary<string, string> attributes)
133 {
134 byte[] tableName = Encode(table);
135 byte[] start = Encode(startRow);
136 List<byte[]> encodedColumns = EncodeStringList(columns);
137 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
138 return client.scannerOpenTs(tableName, start, encodedColumns, timestamp, encodedAttributes);
139 }
140
141 public override int ScannerOpenTs(string table, string startRow, stringstopRow, List<string> columns, long timestamp, Dictionary<string, string> attributes)
142 {
143 byte[] tableName = Encode(table);
144 byte[] start = Encode(startRow);
145 byte[] stop = Encode(stopRow);
146 List<byte[]> encodedColumns = EncodeStringList(columns);
147 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
148 return client.scannerOpenWithStopTs(tableName, start, stop, encodedColumns, timestamp, encodedAttributes);
149 }
150
151 public override List<TRowResult> ScannerGetList(int id, int nbRows)
152 {
153 return client.scannerGetList(id, nbRows);
154 }
155
156 public override List<TRowResult> ScannerGet(int id)
157 {
158 return client.scannerGet(id);
159 }
160
161 public override List<TRowResult> GetRow(string table, string row, Dictionary<string, string> attributes)
162 {
163 byte[] tableName = Encode(table);
164 byte[] startRow = Encode(row);
165 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
166 return client.getRow(tableName, startRow, encodedAttributes);
167 }
168
169 public override List<TRowResult> GetRows(string table, List<string> rows, Dictionary<string, string> attributes)
170 {
171 byte[] tableName = Encode(table);
172 List<byte[]> encodedRows = EncodeStringList(rows);
173 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
174 return client.getRows(tableName, encodedRows, encodedAttributes);
175 }
176
177 public override List<TRowResult> GetRowsWithColumns(string table, List<string> rows, List<string> columns, Dictionary<string, string> attributes)
178 {
179 byte[] tableName = Encode(table);
180 List<byte[]> encodedRows = EncodeStringList(rows);
181 List<byte[]> encodedColumns = EncodeStringList(columns);
182 Dictionary<byte[], byte[]> encodedAttributes = EncodeAttributes(attributes);
183 return client.getRowsWithColumns(tableName, encodedRows, encodedColumns, encodedAttributes);
184 }
185
186 public override void ScannerClose(int id)
187 {
188 client.scannerClose(id);
189 }
190
191 public override void IterateResults(TRowResult result)
192 {
193 foreach (KeyValuePair<byte[], TCell> pair in result.Columns)
194 {
195 Console.WriteLine("\tCol=" + Decode(pair.Key) + ", Value=" + Decode(pair.Value.Value));
196 }
197 }
198
199 private String Decode(byte[] bs)
200 {
201 return UTF8Encoding.Default.GetString(bs);
202 }
203
204 private byte[] Encode(String str)
205 {
206 return UTF8Encoding.Default.GetBytes(str);
207 }
208
209 private Dictionary<byte[], byte[]> EncodeAttributes(Dictionary<String, String> attributes)
210 {
211 Dictionary<byte[], byte[]> encodedAttributes = new Dictionary<byte[],byte[]>();
212 foreach (KeyValuePair<String, String> pair in attributes)
213 {
214 encodedAttributes.Add(Encode(pair.Key), Encode(pair.Value));
215 }
216 return encodedAttributes;
217 }
218
219 private List<byte[]> EncodeStringList(List<String> strings)
220 {
221 List<byte[]> list = new List<byte[]>();
222 if (strings != null)
223 {
224 foreach (String str in strings)
225 {
226 list.Add(Encode(str));
227 }
228 }
229 return list;
230 }
231 }
232 }

上面代码,给出了基本的实现,接着我们给出测试用例,调用我们实现的客户端操作,与HBase表进行交互。实现的测试用例类如下所示:

001 using System;
002 using System.Collections.Generic;
003 using System.Linq;
004 using System.Text;
005 using System.Threading.Tasks;
006
007 namespace HbaseThrift.HBase.Thrift
008 {
009 class Test
010 {
011 private readonly AbstractHBaseThriftService client;
012
013 public Test(String host, int port)
014 {
015 client = new HBaseThriftClient(host, port);
016 }
017
018 public Test() : this("master", 9090)
019 {
020
021 }
022
023 static String RandomlyBirthday()
024 {
025 Random r = new Random();
026 int year = 1900 + r.Next(100);
027 int month = 1 + r.Next(12);
028 int date = 1 + r.Next(30);
029 return year + "-" + month.ToString().PadLeft(2, '0') + "-" + date.ToString().PadLeft(2, '0');
030 }
031
032 static String RandomlyGender()
033 {
034 Random r = new Random();
035 int flag = r.Next(2);
036 return flag == 0 ? "M" : "F";
037 }
038
039 static String RandomlyUserType()
040 {
041 Random r = new Random();
042 int flag = 1 + r.Next(10);
043 return flag.ToString();
044 }
045
046 public void Close()
047 {
048 client.Close();
049 }
050
051 public void CaseForUpdate() {
052 bool writeToWal = false;
053 Dictionary<String, String> attributes = new Dictionary<String, String>(0);
054 string table = SetTable();
055 // put kv pairs
056 for (int i = 0; i < 10000000; i++) {
057 string rowKey = i.ToString().PadLeft(4, '0');
058 Dictionary<String, String> fieldNameValues = new Dictionary<String, String>();
059 fieldNameValues.Add("info:birthday", RandomlyBirthday());
060 fieldNameValues.Add("info:user_type", RandomlyUserType());
061 fieldNameValues.Add("info:gender", RandomlyGender());
062 client.Update(table, rowKey, writeToWal, fieldNameValues, attributes);
063 }
064 }
065
066 public void CaseForDeleteCells() {
067 bool writeToWal = false;
068 Dictionary<String, String> attributes = new Dictionary<String, String>(0);
069 String table = SetTable();
070 // put kv pairs
071 for (long i = 5; i < 10; i++) {
072 String rowKey = i.ToString().PadLeft(4, '0');
073 List<String> columns = new List<String>(0);
074 columns.Add("info:birthday");
075 client.DeleteCells(table, rowKey, writeToWal, columns, attributes);
076 }
077 }
078
079 public void CaseForDeleteRow() {
080 Dictionary<String, String> attributes = new Dictionary<String, String>(0);
081 String table = SetTable();
082 // delete rows
083 for (long i = 5; i < 10; i++) {
084 String rowKey = i.ToString().PadLeft(4, '0');
085 client.DeleteRow(table, rowKey, attributes);
086 }
087 }
088
089 public void CaseForScan() {
090 Dictionary<String, String> attributes = new Dictionary<String, String>(0);
091 String table = SetTable();
092 String startRow = "0005";
093 String stopRow = "0015";
094 List<String> columns = new List<String>(0);
095 columns.Add("info:birthday");
096 int id = client.ScannerOpen(table, startRow, stopRow, columns, attributes);
097 int nbRows = 2;
098 List<TRowResult> results = client.ScannerGetList(id, nbRows);
099 while(results != null) {
100 foreach(TRowResult result in results) {
101 client.IterateResults(result);
102 }
103 results = client.ScannerGetList(id, nbRows);
104 }
105 client.ScannerClose(id);
106 }
107
108 public void CaseForGet() {
109 Dictionary<String, String> attributes = new Dictionary<String, String>(0);
110 String table = SetTable();
111 List<String> rows = new List<String>(0);
112 rows.Add("0009");
113 rows.Add("0098");
114 rows.Add("0999");
115 List<String> columns = new List<String>(0);
116 columns.Add("info:birthday");
117 columns.Add("info:gender");
118 List<TRowResult> results = client.GetRowsWithColumns(table, rows, columns, attributes);
119 foreach(TRowResult result in results) {
120 client.IterateResults(result);
121 }
122 }
123
124 private string SetTable()
125 {
126 string table = "test_info";
127 return table;
128 }
129
130 static void Main(string[] args)
131 {
132 Test test = new Test();
133 //test.CaseForUpdate(); // insert or update rows/cells
134 //test.CaseForDeleteCells(); // delete cells
135 //test.CaseForDeleteRow(); // delete rows
136 test.CaseForScan(); // scan rows
137 //test.CaseForGet(); // get rows
138
139 test.Close();
140 }
141
142 }
143 }

上面的测试可以实现操作Hbase表数据。另外,在生成的Thrift客户端代码中,Iface中给出了全部的服务接口,可以根据需要来选择,客户端Client实现了与Thrift交互的一些逻辑的处理,通过该类对象可以代理HBase提供的Thrift服务。

相关实践学习
lindorm多模间数据无缝流转
展现了Lindorm多模融合能力——用kafka API写入,无缝流转在各引擎内进行数据存储和计算的实验。
云数据库HBase版使用教程
&nbsp; 相关的阿里云产品:云数据库 HBase 版 面向大数据领域的一站式NoSQL服务,100%兼容开源HBase并深度扩展,支持海量数据下的实时存储、高并发吞吐、轻SQL分析、全文检索、时序时空查询等能力,是风控、推荐、广告、物联网、车联网、Feeds流、数据大屏等场景首选数据库,是为淘宝、支付宝、菜鸟等众多阿里核心业务提供关键支撑的数据库。 了解产品详情:&nbsp;https://cn.aliyun.com/product/hbase &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
7月前
|
存储 安全 数据库连接
C#深度揭秘:常量的魅力和实践,一文让你从新手到专家
C#深度揭秘:常量的魅力和实践,一文让你从新手到专家
55 0
|
7月前
|
网络协议 API C#
C# 中模拟 POST 和 GET 请求的原理与实践
【1月更文挑战第4天】在现代网络应用中,HTTP请求是客户端与服务器交互的基础。其中,GET和POST是最常用的两种请求方法。本文将介绍如何使用C#语言模拟这两种请求,并解释其背后的工作原理。我们将利用.NET框架中的HttpClient类来发送请求,并处理服务器的响应。通过本文,读者将能够理解HTTP请求的基本构成,学会在C#中编写代码来模拟这些请求,进而在开发过程中实现与Web服务的交互。
|
4月前
|
开发框架 安全 .NET
全面掌握C#中的类型转换:详解与实践
【8月更文挑战第20天】
165 0
|
2月前
|
测试技术 C# 数据库
C# 一分钟浅谈:测试驱动开发 (TDD) 实践
【10月更文挑战第18天】测试驱动开发(TDD)是一种软件开发方法论,强调先编写测试代码再编写功能代码,以确保代码质量和可维护性。本文从 TDD 的基本概念入手,详细介绍了其核心步骤——编写测试、运行测试并失败、编写代码使测试通过,以及“红绿重构”循环。文章还探讨了 TDD 的优势,包括提高代码质量、促进设计思考、减少调试时间和文档化。此外,文中分析了常见问题及解决方案,如测试覆盖率不足、测试代码过于复杂、忽视重构和测试依赖过多,并通过一个简单的计算器类的代码案例,展示了 TDD 的实际应用过程。
39 1
|
7月前
|
分布式计算 Hadoop Shell
熟悉常用的HBase操作
熟悉常用的HBase操作
152 3
熟悉常用的HBase操作
|
2月前
|
存储 JSON API
HTTP 请求与响应处理:C#中的实践
【10月更文挑战第4天】在现代Web开发中,HTTP协议至关重要,无论构建Web应用还是API开发,都需要熟练掌握HTTP请求与响应处理。本文从C#角度出发,介绍HTTP基础知识,包括请求与响应结构,并通过`HttpClient`库演示如何发送GET请求及处理响应,同时分析常见错误并提供解决方案,助你更高效地完成HTTP相关任务。
95 2
|
2月前
|
数据采集 C# 数据库
数据验证与错误处理:C#中的实践
【10月更文挑战第1天】在软件开发中,数据验证与错误处理至关重要,不仅能提升程序的健壮性和安全性,还能改善用户体验。本文从基础概念入手,详细介绍了C#中的数据验证方法,包括使用自定义属性和静态方法验证数据,以及常见的错误处理技巧,如Try-Catch-Finally结构和自定义异常。通过具体示例,帮助读者掌握最佳实践,构建高质量应用。
95 3
|
2月前
|
开发框架 缓存 算法
开源且实用的C#/.NET编程技巧练习宝库(学习,工作,实践干货)
开源且实用的C#/.NET编程技巧练习宝库(学习,工作,实践干货)
|
3月前
|
SQL 开发框架 安全
并发集合与任务并行库:C#中的高效编程实践
在现代软件开发中,多核处理器普及使多线程编程成为提升性能的关键。然而,传统同步模型在高并发下易引发死锁等问题。为此,.NET Framework引入了任务并行库(TPL)和并发集合,简化并发编程并增强代码可维护性。并发集合允许多线程安全访问,如`ConcurrentQueue&lt;T&gt;`和`ConcurrentDictionary&lt;TKey, TValue&gt;`,有效避免数据不一致。TPL则通过`Task`类实现异步操作,提高开发效率。正确使用这些工具可显著提升程序性能,但也需注意任务取消和异常处理等常见问题。
50 1
|
3月前
|
存储 C#
C# 一分钟浅谈:继承与多态性的实践
【9月更文挑战第2天】本文从基础入手,详细介绍了面向对象编程中继承与多态性的核心概念。通过 `Animal`、`Dog` 和 `Cat` 类的示例代码,展示了如何利用继承重用代码及多态性实现不同对象对同一方法的多样化响应,帮助读者更好地理解和应用这两个重要概念,提升面向对象编程能力。
44 3