Skip to content

Commit 3a98217

Browse files
committed
Add MySqlCommandBuilder. Fixes #303
1 parent 1a2e63a commit 3a98217

File tree

3 files changed

+203
-13
lines changed

3 files changed

+203
-13
lines changed
Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
1-
using System.Data.Common;
1+
using System.Data.Common;
22

33
namespace MySql.Data.MySqlClient
44
{
55
public sealed class MySqlClientFactory : DbProviderFactory
66
{
77
public static readonly MySqlClientFactory Instance = new MySqlClientFactory();
88

9+
public override DbCommand CreateCommand() => new MySqlCommand();
10+
public override DbConnection CreateConnection() => new MySqlConnection();
11+
public override DbConnectionStringBuilder CreateConnectionStringBuilder() => new MySqlConnectionStringBuilder();
12+
public override DbParameter CreateParameter() => new MySqlParameter();
13+
14+
#if !NETSTANDARD1_3
15+
public override DbCommandBuilder CreateCommandBuilder() => new MySqlCommandBuilder();
16+
public override DbDataAdapter CreateDataAdapter() => new MySqlDataAdapter();
17+
#endif
18+
919
private MySqlClientFactory()
1020
{
1121
}
12-
13-
public override DbCommand CreateCommand()
14-
=> new MySqlCommand();
15-
16-
public override DbConnection CreateConnection()
17-
=> new MySqlConnection();
18-
19-
public override DbConnectionStringBuilder CreateConnectionStringBuilder()
20-
=> new MySqlConnectionStringBuilder();
21-
22-
public override DbParameter CreateParameter()
23-
=> new MySqlParameter();
2422
}
2523
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#if !NETSTANDARD1_3
2+
using System;
3+
using System.Data;
4+
using System.Data.Common;
5+
using MySqlConnector.Utilities;
6+
7+
namespace MySql.Data.MySqlClient
8+
{
9+
public class MySqlCommandBuilder : DbCommandBuilder
10+
{
11+
public MySqlCommandBuilder()
12+
{
13+
QuotePrefix = "`";
14+
QuoteSuffix = "`";
15+
}
16+
17+
public MySqlCommandBuilder(MySqlDataAdapter dataAdapter)
18+
: this()
19+
{
20+
DataAdapter = dataAdapter;
21+
}
22+
23+
public new MySqlDataAdapter DataAdapter
24+
{
25+
get => (MySqlDataAdapter) base.DataAdapter;
26+
set => base.DataAdapter = value;
27+
}
28+
29+
public new MySqlCommand GetDeleteCommand() => (MySqlCommand) base.GetDeleteCommand();
30+
public new MySqlCommand GetInsertCommand() => (MySqlCommand) base.GetInsertCommand();
31+
public new MySqlCommand GetUpdateCommand() => (MySqlCommand) base.GetUpdateCommand();
32+
33+
protected override void ApplyParameterInfo(DbParameter parameter, DataRow row, StatementType statementType, bool whereClause)
34+
{
35+
((MySqlParameter) parameter).MySqlDbType = (MySqlDbType) row[SchemaTableColumn.ProviderType];
36+
}
37+
38+
protected override string GetParameterName(int parameterOrdinal) => "@p{0}".FormatInvariant(parameterOrdinal);
39+
protected override string GetParameterName(string parameterName) => "@" + parameterName;
40+
protected override string GetParameterPlaceholder(int parameterOrdinal) => "@p{0}".FormatInvariant(parameterOrdinal);
41+
42+
protected override void SetRowUpdatingHandler(DbDataAdapter adapter)
43+
{
44+
if (!(adapter is MySqlDataAdapter mySqlDataAdapter))
45+
throw new ArgumentException("adapter needs to be a MySqlDataAdapter", nameof(adapter));
46+
47+
if (adapter == DataAdapter)
48+
mySqlDataAdapter.RowUpdating -= RowUpdatingHandler;
49+
else
50+
mySqlDataAdapter.RowUpdating += RowUpdatingHandler;
51+
}
52+
53+
public override string QuoteIdentifier(string unquotedIdentifier) => QuotePrefix + unquotedIdentifier.Replace("`", "``") + QuoteSuffix;
54+
55+
public override string UnquoteIdentifier(string quotedIdentifier)
56+
{
57+
if (quotedIdentifier.Length >= 2 && quotedIdentifier[0] == QuotePrefix[0] && quotedIdentifier[quotedIdentifier.Length - 1] == QuoteSuffix[0])
58+
quotedIdentifier = quotedIdentifier.Substring(1, quotedIdentifier.Length - 2);
59+
return quotedIdentifier.Replace("``", "`");
60+
}
61+
62+
private void RowUpdatingHandler(object sender, MySqlRowUpdatingEventArgs e) => RowUpdatingHandler(e);
63+
}
64+
}
65+
#endif
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#if !NETCOREAPP1_1_2
2+
using System;
3+
using System.Data;
4+
using Dapper;
5+
using MySql.Data.MySqlClient;
6+
using Xunit;
7+
8+
namespace SideBySide
9+
{
10+
public class CommandBuilderTests : IClassFixture<DatabaseFixture>, IDisposable
11+
{
12+
public CommandBuilderTests(DatabaseFixture database)
13+
{
14+
m_database = database;
15+
m_database.Connection.Open();
16+
}
17+
18+
public void Dispose()
19+
{
20+
m_database.Connection.Close();
21+
}
22+
23+
[Fact]
24+
public void Insert()
25+
{
26+
m_database.Connection.Execute(@"drop table if exists command_builder_insert;
27+
create table command_builder_insert
28+
(
29+
id int not null primary key,
30+
value varchar(100)
31+
);");
32+
using (var dataAdapter = new MySqlDataAdapter("select * from command_builder_insert", m_database.Connection))
33+
using (new MySqlCommandBuilder(dataAdapter))
34+
{
35+
var dataTable = new DataTable();
36+
dataAdapter.Fill(dataTable);
37+
38+
var row = dataTable.NewRow();
39+
row["id"] = 1;
40+
row["value"] = "inserted";
41+
dataTable.Rows.Add(row);
42+
43+
dataAdapter.Update(dataTable);
44+
}
45+
46+
Assert.Equal(1, m_database.Connection.ExecuteScalar<int>("select count(*) from command_builder_insert;"));
47+
Assert.Equal("inserted", m_database.Connection.ExecuteScalar<string>("select value from command_builder_insert where id = 1;"));
48+
}
49+
50+
[Fact]
51+
public void Update()
52+
{
53+
m_database.Connection.Execute(@"drop table if exists command_builder_update;
54+
create table command_builder_update
55+
(
56+
id int not null primary key,
57+
value varchar(100)
58+
);
59+
insert into command_builder_update values(1, 'one'), (2, 'two');
60+
");
61+
using (var dataAdapter = new MySqlDataAdapter("select * from command_builder_update", m_database.Connection))
62+
using (new MySqlCommandBuilder(dataAdapter))
63+
{
64+
var dataTable = new DataTable();
65+
dataAdapter.Fill(dataTable);
66+
67+
dataTable.Rows[0]["value"] = "updated";
68+
dataAdapter.Update(dataTable);
69+
}
70+
71+
Assert.Equal("updated", m_database.Connection.ExecuteScalar<string>("select value from command_builder_update where id = 1;"));
72+
}
73+
74+
[Fact]
75+
public void Delete()
76+
{
77+
m_database.Connection.Execute(@"drop table if exists command_builder_delete;
78+
create table command_builder_delete
79+
(
80+
id int not null primary key,
81+
value varchar(100)
82+
);
83+
insert into command_builder_delete values(1, 'one'), (2, 'two');
84+
");
85+
using (var dataAdapter = new MySqlDataAdapter("select * from command_builder_delete", m_database.Connection))
86+
using (new MySqlCommandBuilder(dataAdapter))
87+
{
88+
var dataTable = new DataTable();
89+
dataAdapter.Fill(dataTable);
90+
91+
dataTable.Rows[0].Delete();
92+
93+
dataAdapter.Update(dataTable);
94+
}
95+
96+
Assert.Equal(1, m_database.Connection.ExecuteScalar<int>("select count(*) from command_builder_delete;"));
97+
}
98+
99+
[Theory]
100+
[InlineData("test", "`test`")]
101+
[InlineData("te`st", "`te``st`")]
102+
[InlineData("`test`", "```test```"
103+
#if BASELINE
104+
, Skip = "Doesn't quote leading quotes"
105+
#endif
106+
)]
107+
public void QuoteIdentifier(string input, string expected)
108+
{
109+
var cb = new MySqlCommandBuilder();
110+
Assert.Equal(expected, cb.QuoteIdentifier(input));
111+
}
112+
113+
[Theory]
114+
[InlineData("test", "test")]
115+
[InlineData("`test`", "test")]
116+
[InlineData("`te``st`", "te`st")]
117+
[InlineData("```test```", "`test`")]
118+
public void UnquoteIdentifier(string input, string expected)
119+
{
120+
var cb = new MySqlCommandBuilder();
121+
Assert.Equal(expected, cb.UnquoteIdentifier(input));
122+
}
123+
124+
readonly DatabaseFixture m_database;
125+
}
126+
}
127+
#endif

0 commit comments

Comments
 (0)