SQLCE - Upsert(更新或插入) - 如何使用常用方法准备一行?
Posted
技术标签:
【中文标题】SQLCE - Upsert(更新或插入) - 如何使用常用方法准备一行?【英文标题】:SQLCE - Upsert (Update or Insert) - How to prepare a row using common method? 【发布时间】:2011-02-16 12:12:30 【问题描述】:以下是伪代码:
SqlCeResultSet myResultSet = cmd.ExecuteResultSet(Options...etc);
bool found = myResultSet.Seek();
if found
//do an Update
myResultSet.Read() //make current
//At this point we have a cursor positioned at a row to be edited
myResultSet.SetString(1, "value for col 1");
myResultSet.SetString(2, "value for col 2");
//... etc...
myResultSet.SetString(100, "value for col 100");
//i want to replace above with: CommonMethodToFillRowData(someRow)
//finally update
myResultSet.Update();
else
//do an insert
SqlCeUpdatableRecord myRec = myResultSet.CreateRecord();
//set primaryKey
myRec.SetInt32(0, pkValue);
//At this point we have a cursor positioned at a row to be edited
myRec.SetString(1, "value for col 1");
myRec.SetString(2, "value for col 2");
//... etc...
myRec.SetString(100, "value for col 100");
//i want to replace above with: CommonMethodToFillRowData(someRow)
//finally insert
myResultSet.Insert(myRec);
从上面看,如果我有 100 列要准备,则必须重复两次;我想要的是一些 CommonMethodToFillRowData();但是对于这种方法,我使用什么类型的参数?
CommonMethodToFillRowData(SqlCeResultSet or SqlCeUpdatableRecord ? parmRow)
parmRow.SetInt32(col1, value1)
parmRow.SetString(col2, value2)
...etc.
parmRow.SetString(100, "value for col 100");
在 SqlCeUpdatableRecord 类上直接引用 MSDN doco: --> 表示来自数据源的一行可更新值。 SqlCeResultSet 对象包含一个或多个 UpdatableRecords。
如果是这样,为什么我不能直接访问 SqlCeResultSet 中的单个 UpdatableRecord,一旦我通过 Seek() 定位光标?
如果可能的话,那将使我能够使用:
CommonMethodToFillRowData(SqlCeUpdatableRecord parmRow)
//end of story
【问题讨论】:
这个 JoeDotNot 你是怎么相处的??? @Sébastien - 请查看我的“最后一分钟”回答。 【参考方案1】:创建一个可以表示SqlCeResultSet
或SqlCeUpdatableRecord
的包装器对象(视情况而定)。然后编写一次“保存”代码并将其应用为更新或插入,具体取决于是否发现记录已存在。
警告:这是未经测试的代码。
public void SavingMyData()
SqlCeResultSet resultSet = cmd.ExecuteResultSet(Options...etc);
SqlCeWrapper wrapper = new SqlCeWrapper(resultSet);
wrapper.SetInt32(0, pkValue, true); // Primary Key = true
wrapper.SetString(1, "value for col 1");
wrapper.SetString(2, "value for col 2");
wrapper.SetString(100, "value for col 100");
wrapper.Commit();
...
public class SqlCeWrapper
private readonly bool _found;
private readonly SqlCeResultSet _resultSet;
private readonly SqlCeUpdatableRecord _newRecord;
public SqlCeWrapper(SqlCeResultSet resultSet)
_resultSet = resultSet;
_found = resultSet.Seek();
if (_found)
resultSet.Read();
else
_newRecord = resultSet.CreateRecord();
public void SetInt32(int ordinal, int value, bool isPrimary = false)
if (_found && !isPrimary)
_resultSet.SetInt32(ordinal, value);
else if (!_found)
_newRecord.SetInt32(ordinal, value);
public void SetString(int ordinal, string value, bool isPrimary = false)
if (_found && !isPrimary)
_resultSet.SetString(ordinal, value);
else if (!_found)
_newRecord.SetString(ordinal, value);
public void Commit()
if (_found)
_resultSet.Update();
else
_resultSet.Insert(_newRecord);
注意:如果您不使用 .NET 4,则必须删除可选参数。您还可以根据需要在SqlCeWrapper
中添加额外的SetX()
方法。
【讨论】:
谢谢@Greg。您的答案与@Calvin 的答案相同。它可以完成这项工作,只需要编写一次,并且在使用时非常优雅。我不确定性能,但在这种情况下可能根本不是问题。在你的答案和@WarrenG 之间做出艰难的选择,但我选择了@WarrenG,他真的以最简单的方式回答了这个问题。无论如何,谢谢! @Greg,感谢您发布这个优雅的解决方案!我很久以前就放弃了它无法完成并将我的代码作为两个分支,但我终于有时间查看您的答案并且它可以 100% 工作。给你正确答案!【参考方案2】:怎么样:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlServerCe;
using System.Collections;
using System.Data;
namespace SqlCeRecord_Test
class Program
static void Main(string[] args)
// Arguments for update
int lookFor = 1;
string value = "AC/DC";
// Arguments for insert
lookFor = Int16.MaxValue;
value = "joedotnet";
using (SqlCeConnection conn = new SqlCeConnection(@"Data Source=C:\Users\xeej\Downloads\ChinookPart2\Chinook.sdf"))
conn.Open();
using (SqlCeCommand cmd = new SqlCeCommand("Artist"))
SqlCeUpdatableRecord myRec = null;
cmd.Connection = conn;
cmd.CommandType = System.Data.CommandType.TableDirect;
cmd.IndexName = "PK_Artist";
SqlCeResultSet myResultSet = cmd.ExecuteResultSet(ResultSetOptions.Updatable | ResultSetOptions.Scrollable);
bool found = myResultSet.Seek(DbSeekOptions.FirstEqual, new object[] lookFor );
if (found)
myResultSet.Read();
else
myRec = myResultSet.CreateRecord();
foreach (KeyValuePair<int, object> item in CommonMethodToFillRowData(value))
if (found)
myResultSet.SetValue(item.Key, item.Value);
else
myRec.SetValue(item.Key, item.Value);
if (found)
myResultSet.Update();
else
myResultSet.Insert(myRec);
private static Dictionary<int, object> CommonMethodToFillRowData(string value1) //TODO add more values
var dict = new Dictionary<int, object>();
dict.Add(1, value1);
return dict;
【讨论】:
不完全是我的想法:) 虽然我同意@WarrenG 对此的回答,但我要感谢您花在这个赏金上的时间。【参考方案3】:如何在 SqlCeResultSet 上使用枚举器而不是像这样的 Read 方法
IEnumerator enumerator = myResultSet.GetEnumerator();
bool found = enumerator.MoveNext();
SqlCeUpdatableRecord record;
if (found)
record = (SqlCeUpdatableRecord)enumerator.Current;
MethodToFill(record);
myResultSet.Update();
else
record = myResultSet.CreateRecord();
MethodToFill(record);
myResultSet.Insert(record);
private void MethodToFill(SqlCeUpdatableRecord recordToFill)
recordToFill.SetString(0, "Hello");
recordToFill.SetString(1, "World");
// etc
【讨论】:
这正是我一直在寻找的答案(我认为@JoeDotNot 也在寻找答案)。谢谢@WarrenG! 对不起,当赏金即将结束时,我处于 AFK 状态,这似乎起到了作用。唉,经过测试,这似乎在现实生活中不起作用。我把赏金给了错误的人......如果我能改变它,我会选择@Greg的答案...... 我终于开始审查答案了,因为我很久以前就放弃了它无法完成,并将我的代码保留为两个分支。 @WarrenG:感谢您的努力,您的答案看起来很简单,直到我在 MSDN 上遇到这个问题“枚举器可用于读取集合中的数据,但它们不能用于修改基础集合。” @Sebastien 你太快扣动扳机了,哈哈,我不赞成这个答案以对抗一些(错误授予的)赏金积分:-) @joedot 不对,我没有注意枚举器的限制。要放弃另一种可能的解决方案,如果您使用的是 C# 4,则可以将填充方法的签名更改为MehodToFill(dynamic objectToFill)
,然后使用上面的原始代码,您可以传递 SqlCeResultSet 或 SqlCeUpdateableRecord 并且将被填充适当的。【参考方案4】:
我不确定您要做什么,也不确定具体的这些组件,因此可能有一种更有效的方法。但是根据MSDN,SqlCeResultSet是一个SqlCeDataReader,也就是一个DbDataReader,它实现了IDataRecord...而SqlCeUpdateableRecord也实现了IDataRecord。
那么,这行得通吗?
public static void DoStuff()
SqlCeCommand cmd = new SqlCeCommand();
SqlCeResultSet myResultSet = cmd.ExecuteResultSet(ResultSetOptions.None);
var reader = myResultSet.Read();
bool found = myResultSet.Seek(DbSeekOptions.After);
if (found)
myResultSet.Read();
CommonMethodToFillRowData(myResultSet);
myResultSet.Update();
else
SqlCeUpdatableRecord myRec = myResultSet.CreateRecord();
CommonMethodToFillRowData(myRec);
myResultSet.Insert(myRec);
// All the messy Type-wrangling is hidden behind the scenes
public static void CommonMethodToFillRowData(this IDataRecord RowToFill)
RowToFill.SetInt32(1, 42);
RowToFill.SetString(2, "Foo");
// etc...
// Since SetInt32 seems to do the same thing in either inherited Type
public static void SetInt32(this IDataRecord RowToFill, int Ordinal, int Value)
Type rowType = RowToFill.GetType();
if (rowType == typeof(SqlCeResultSet))
((SqlCeResultSet)RowToFill).SetInt32(Ordinal, Value);
else if (rowType == typeof(SqlCeUpdatableRecord))
((SqlCeUpdatableRecord)RowToFill).SetInt32(Ordinal, Value);
else
throw new ArgumentException("Method does not know what to do with Type " + rowType.ToString());
// Since SetString seems to do the same thing in either inherited Type
public static void SetString(this IDataRecord RowToFill, int Ordinal, string Value)
Type rowType = RowToFill.GetType();
if (rowType == typeof(SqlCeResultSet))
((SqlCeResultSet)RowToFill).SetString(Ordinal, Value);
else if (rowType == typeof(SqlCeUpdatableRecord))
((SqlCeUpdatableRecord)RowToFill).SetString(Ordinal, Value);
else
throw new ArgumentException("Method does not know what to do with Type " + rowType.ToString());
这很糟糕,性能也不会很好,但如果您只想将行填充到一段代码中,它可能会满足您的需求。
【讨论】:
感谢您的回答@Calvin。 @Greg 提供了一个基于您的更优雅的解决方案,但最后我选择了@WarrenG 的答案,这是我认为最简单的答案。谢谢@Calvin!以上是关于SQLCE - Upsert(更新或插入) - 如何使用常用方法准备一行?的主要内容,如果未能解决你的问题,请参考以下文章