SQLCE - Upsert (Update или Insert) - Как подготовить строку, используя общий метод? - PullRequest
8 голосов
/ 16 февраля 2011

Следующий псевдокод:

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");
}

Прямое цитирование из MSDN doco в классе SqlCeUpdatableRecord: -> Представляет строку обновляемых значений из источника данных. Объект SqlCeResultSet содержит один или несколько UpdatableRecords.

Если это так, то почему я не могу иметь прямой доступ к одному UpdatableRecord внутри SqlCeResultSet, как только я наведу курсор с помощью Seek ()?

Если бы это было возможно, это позволило бы мне использовать:

CommonMethodToFillRowData(SqlCeUpdatableRecord parmRow) {
   //end of story
}

Ответы [ 4 ]

3 голосов
/ 07 апреля 2011

Создайте объект-оболочку, который может представлять SqlCeResultSet или SqlCeUpdatableRecord в зависимости от ситуации.Затем напишите свой код «сохранения» один раз и примените его как обновление или вставку, в зависимости от того, была ли найдена запись, уже существующая.

Предупреждение: Это кодuntested.

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, вам придется удалить необязательные параметры.Вы также можете добавить дополнительные SetX() методы к SqlCeWrapper в соответствии с вашими потребностями.

3 голосов
/ 04 апреля 2011

Как насчет:

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;
        } 
    }
}
2 голосов
/ 07 апреля 2011

Как насчет использования перечислителя в 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
}
1 голос
/ 06 апреля 2011

Я не уверен, что вы пытаетесь сделать, и не уверен насчет этих компонентов конкретно, поэтому, возможно, существует более эффективный подход в целом. Но согласно MSDN, SqlCeResultSet является SqlCeDataReader , который является DbDataReader , который реализует IDataRecord ... и SqlCeUpdateRec также реализует 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());
}

Это неприятно, и производительность не будет отличной, но она может сделать то, что вы ищете, если все, что вы хотите сделать, - это свернуть заполнение строк в один раздел кода.

...