2011-02-16 14 views
8

siguiente es el pseudo-código:SQLCE - Upsert (Actualizar o Insertar) - ¿Cómo preparar una fila usando un método común?

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

partir de lo anterior, si he 100 columnas para preparar, tiene que ser repetido dos veces; Lo que quiero es algún CommonMethodToFillRowData(); Pero, ¿qué tipo de parámetro utilizo para dicho método?

CommonMethodToFillRowData(SqlCeResultSet or SqlCeUpdatableRecord ? parmRow) { 
    parmRow.SetInt32(col1, value1) 
    parmRow.SetString(col2, value2) 
    ...etc. 
    parmRow.SetString(100, "value for col 100"); 
} 

citando Directamente desde MSDN mana en SqlCeUpdatableRecord Clase: -> Representa una fila de valores actualizables desde la fuente de datos. Un objeto SqlCeResultSet contiene uno o más UpdatableRecords.

Si ese es el caso, ¿por qué no puedo tener acceso directo a un único UpdatableRecord dentro de SqlCeResultSet, una vez que coloco el cursor a través de una Seek()?

Si eso fuera posible, eso me habilitado para usar:

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

¿Cómo te fue con este JoeDotNot ??? – ETFairfax

+0

Estoy interesado también ... –

+0

@ Sébastien - Consulte mi respuesta de "último minuto". – Greg

Respuesta

3

Crear un objeto de contenedor que puede representar tanto SqlCeResultSet o SqlCeUpdatableRecord según corresponda. Luego, escriba su código de "guardar" una vez y haga que se aplique como una actualización o una inserción, según si el registro ya se encontró.

Advertencia: Este es el código no probado.

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

Nota: Si no está utilizando .NET 4, usted tiene que quitar los parámetros opcionales. También puede agregar métodos adicionales SetX() al SqlCeWrapper según sus necesidades.

+0

Gracias @Greg. Tu respuesta es del mismo tipo que la de @ Calvin. Haría el trabajo, necesitaría escribirse solo una vez y es bastante elegante cuando se usa. No estoy seguro sobre el rendimiento, pero puede no ser un problema en este caso. Fue una decisión difícil entre su respuesta y @ WarrenG, pero fui con @WarrenG quien realmente respondió la pregunta de la manera más simple. Gracias de todos modos ! –

+0

@Greg, ¡gracias por publicar esta elegante solución! Había renunciado hace mucho tiempo a que no se podía hacer y dejé mi código como dos ramas, pero finalmente pude revisar tu respuesta y funciona al 100%. ¡Respuesta correcta para ti! – joedotnot

3

¿Qué tal:

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; 
     } 
    } 
} 
+0

Bueno, todavía tiene que repetir dos veces el bucle foreach ... –

+1

Ahora un solo bucle foreach – ErikEJ

+0

bien no es exactamente lo que estaba pensando :) –

1

No estoy seguro de lo que estás tratando de hacer, y no estoy seguro sobre esos componentes específicamente, por lo que probablemente haya un enfoque más eficiente. Pero según MSDN, SqlCeResultSet es un SqlCeDataReader, que es un DbDataReader, que implementa IDataRecord ... y SqlCeUpdateableRecord también implementa IDataRecord.

¿Funciona?

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

Es desagradable y el rendimiento no será grande, pero podría hacer lo que estás buscando si todo lo que quiere hacer es colapsar la fila de introducirlo en una sección de código.

+0

Gracias por su respuesta @ [email protected] ofreció una solución basada en la tuya que era un poco más elegante, pero al final fui con la respuesta de @ WarrenG que fue la respuesta más simple en mi opinión. Gracias @ Calvin! –

2

¿Y si uso el empadronador en el SqlCeResultSet en lugar del método Read como esto

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 
} 
+0

Esta es exactamente la respuesta que estaba buscando (y creo que la respuesta @JoeDotNot buscaba también). Gracias @ WarrenG! –

+0

¿Puedes hacer que este código funcione, Sebastien? – ErikEJ

+0

Aw lo siento chicos, yo estaba AFK cuando el botín estaba a punto de terminar y esto parecía hacer el truco. Por desgracia, después de las pruebas parece que esto no funciona en la vida real. Me dio la recompensa a la persona equivocada ... Si pudiera cambiarlo Me gustaría ir con @ respuesta de Greg entonces ... –

Cuestiones relacionadas