Update multiple records using dapper after select by uniqueidentifier - c#

I have number of records that I have loaded from database and I want to update set couple values before I return them. The only main requirement I do not want multiple commands updating each record one by one.
Please note my ids are GUIDs(uniqueidentifier)
so far I have my code as
public IEnumerable<Person> GetUnprocessedPeople(int batchSize)
{
List<Queue_ImportQueue> list;
using (IDbConnection db = OpenedConnection)
{
string peopleList = $"SELECT TOP({batchSize}) * FROM [dbo].[Person]";
list = db.Query<Person>(peopleList).ToList();
using (IDbTransaction transactionScope = db.BeginTransaction(IsolationLevel.Serializable))
{
string updateQuery = $"UPDATE [dbo].[Person] SET Created = GETDATE() WHERE Id ='#ids'";
try
{
db.Execute(updateQuery, new { Id = list.Select(x => x.Id) }, transactionScope);
transactionScope.Commit();
}
catch (Exception ex)
{
transactionScope.Rollback();
throw;
}
}
}
return list;
}

OK the solution was quite simple.
There is no need for specific casting.
just ensure you have all arguments correct.
So extract that has fixed the query for me:
string updateQuery = $"UPDATE [dbo].[Person] SET Created = GETDATE() WHERE Id ='#Ids'";
...
db.Execute(updateQuery, new { Ids = list.Select(x => x.Id) }, transactionScope);

Related

SQL ERROR System.Collections.Generic.List`1[Dapper.SqlMapper+DapperRow]

I would like my code to return a value but it returns me:
System.Collections.Generic.List`1[Dapper.SqlMapper+DapperRow]
I can't solve and make a right output process.
public static string Test()
{
using (IDbConnection cnn = new SQLiteConnection(LoadConnectionString()))
{
var vartest = cnn.Query("select grado from utenti where id='10'");
//var result = output.ToDictionary(row => (string)row.Grado, row => (string)row.Nome ) ; (commento)
//Console.WriteLine(vartest);
cnn.Close();
return vartest.ToString();
}
}
Query and Query<T> return multiple rows; Query is for dynamic rows; Query<T> is for typed rows. There are QueryFirst[<T>] and QuerySingle[<T>] for single rows.
If you're after a single value of a known type, then perhaps:
var vartest = cnn.QuerySingle<string>("select grado from utenti where id='10'");
if you mean your return vartest.ToString(); is returning you the string "System.Collections.Generic.List`1[Dapper.SqlMapper+DapperRow]" it is because your vartest is a list of items, you need to .ToString the item in the list
public static string ManagerFindid()
{
using (IDbConnection cnn = new SQLiteConnection(LoadConnectionString()))
{
var select = cnn.Query("select id from utenti");
if (select.Any())
{
return select[0].ToString();
// or do something with all the items in your list
foreach(string value in select)
{
//add value into list view
}
}
else
{
//this is hit when there are no items returned from the select query
return "Nothing Returned from Query";
}
}
you could also handle there being multiple items returned from your SQL
foreach(string value in select)
{
//do something with current value
}

Extract multiple data rows out of a table in Firebird SQL using C# .NET

I can't find anything specific in the Firebird.NET documentaion, but I need to extract multiple specific data rows out of a table from my Firebird database. I later want to host the server and website formulars, where you can list all the employee with it's specific data fields which i want to choose manually.
The sql query would be:
SELECT employee_id, name, first_name, active_or_not
FROM t_personal
I can already make a query with a single return string inside the server-class using Dapper like this:
using System;
using System.Linq;
using FirebirdSql.Data.FirebirdClient;
using Dapper;
using System.Data;
//the references i use
public string SingleQuery(string commando)
{
try
{
String query = _festeVerbindung.Query<String>(commando).Single();
return query;
}
catch (Exception oof)
{
return oof.Message;
}
}
Preferably i would like to extract them into a string array because i think this would be the best option to put it in an output web formular later, but if you have any better ideas i would appreciate it.
I don't think your query will work as you expect. You are returning one row of type string, but your SQL query is returning multiple fields. If you really want to return a string, then in the SQL statement you can concatenate the fields. But this would make dealing with the individual fields in the C# code harder. I would suggest something along the lines of below. I am illustrating 3 ways to do this. 1) with no where clause in the SQL, then another with a where clause and lastly converting the results to an array or strings. The snippet for the last i wouldn't recommend it, it is sloppy but it is there to show how you can transform the return set into an array of strings. If you need to really return an array of strings i would suggest using a CSV or JSON test serializer. Those packages call handle the anomalies you'd find when dealing with strings (like NULL or embedded commas, etc. ).
-HTH
public class EmployeeRec
{
public int Employee_Id { get; set; }
public string Name { get; set; }
public string First_name { get; set; }
public string active_or_not { get; set; }
}
public class QueryClass
{
public EmployeeRec[] ExecuteQueryNoWhereClause()
{
var sql = #"SELECT employee_id, name, first_name, active_or_not FROM t_personal";
try
{
using (IDbConnection _festeVerbindung = Data.Connection.GetConnection("My Connectionstring"))
{
var results = _festeVerbindung.Query<EmployeeRec>(sql).ToArray();
var activeOnly = results.Where(x => x.active_or_not == "Y");
return results;
}
}
catch (Exception oof)
{
throw;
}
}
public EmployeeRec[] ExecuteQueryWithWhereClause(string activeFlag)
{
var sql = #"SELECT employee_id, name, first_name, active_or_not FROM t_personal WHere active_or_not = #ACTIVE_FLAG";
try
{
using (IDbConnection _festeVerbindung = Data.Connection.GetConnection("My Connectionstring"))
{
var results = _festeVerbindung.Query<EmployeeRec>(sql, new { ACTIVE_FLAG = activeFlag }).ToArray();
foreach (var item in results)
{
}
return results;
}
}
catch (Exception oof)
{
throw;
}
}
public string[] ExecuteQueryReturnStringArray(string activeFlag)
{
StringBuilder sb = new StringBuilder();
List<string> retArray = new List<string>();
var sql = #"SELECT employee_id, name, first_name, active_or_not FROM t_personal WHere active_or_not = #ACTIVE_FLAG";
try
{
using (IDbConnection _festeVerbindung = Data.Connection.GetConnection("My Connectionstring"))
{
var results = _festeVerbindung.Query<EmployeeRec>(sql, new { ACTIVE_FLAG = activeFlag }).ToArray();
/*
*
* There are better ways to do this, if you need to return a string array you should a Json or CSV text serializer like Servicestack.Text *
*
*/
retArray.Add("employee_id, name, first_name, active_or_not");
foreach (var item in results)
{
retArray.Add(item.Employee_Id.ToString() + "," + item.Name + "," + item.First_name + "," + item.active_or_not);
}
return retArray.ToArray();
}
}
catch (Exception oof)
{
throw;
}
}
}

IBM db2Command SQL SELECT * FROM IN #namedParameter

When using the C# code below to construct a DB2 SQL query the result set only has one row. If I manually construct the "IN" predicate inside the cmdTxt string using string.Join(",", ids) then all of the expected rows are returned. How can I return all of the expected rows using the db2Parameter object instead of building the query as a long string to be sent to the server?
public object[] GetResults(int[] ids)
{
var cmdTxt = "SELECT DISTINCT ID,COL2,COL3 FROM TABLE WHERE ID IN ( #ids ) ";
var db2Command = _DB2Connection.CreateCommand();
db2Command.CommandText = cmdTxt;
var db2Parameter = db2Command.CreateParameter();
db2Parameter.ArrayLength = ids.Length;
db2Parameter.DB2Type = DB2Type.DynArray;
db2Parameter.ParameterName = "#ids";
db2Parameter.Value = ids;
db2Command.Parameters.Add(db2Parameter);
var results = ExecuteQuery(db2Command);
return results.ToArray();
}
private object[] ExecuteQuery(DB2Command db2Command)
{
_DB2Connection.Open();
var resultList = new ArrayList();
var results = db2Command.ExecuteReader();
while (results.Read())
{
var values = new object[results.FieldCount];
results.GetValues(values);
resultList.Add(values);
}
results.Close();
_DB2Connection.Close();
return resultList.ToArray();
}
You cannot send in an array as a parameter. You would have to do something to build out a list of parameters, one for each of your values.
e.g.: SELECT DISTINCT ID,COL2,COL3 FROM TABLE WHERE ID IN ( #id1, #id2, ... #idN )
And then add the values to your parameter collection:
cmd.Parameters.Add("#id1", DB2Type.Integer).Value = your_val;
Additionally, there are a few things I would do to improve your code:
Use using statements around your DB2 objects. This will automatically dispose of the objects correctly when they go out of scope. If you don't do this, eventually you will run into errors. This should be done on DB2Connection, DB2Command, DB2Transaction, and DB2Reader objects especially.
I would recommend that you wrap queries in a transaction object, even for selects. With DB2 (and my experience is with z/OS mainframe, here... it might be different for AS/400), it writes one "accounting" record (basically the work that DB2 did) for each transaction. If you don't have an explicit transaction, DB2 will create one for you, and automatically commit after every statement, which adds up to a lot of backend records that could be combined.
My personal opinion would also be to create a .NET class to hold the data that you are getting back from the database. That would make it easier to work with using IntelliSense, among other things (because you would be able to auto-complete the property name, and .NET would know the type of the object). Right now, with the array of objects, if your column order or data type changes, it may be difficult to find/debug those usages throughout your code.
I've included a version of your code that I re-wrote that has some of these changes in it:
public List<ReturnClass> GetResults(int[] ids)
{
using (var conn = new DB2Connection())
{
conn.Open();
using (var trans = conn.BeginTransaction(IsolationLevel.ReadCommitted))
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = trans;
var parms = new List<string>();
var idCount = 0;
foreach (var id in ids)
{
var parm = "#id" + idCount++;
parms.Add(parm);
cmd.Parameters.Add(parm, DB2Type.Integer).Value = id;
}
cmd.CommandText = "SELECT DISTINCT ID,COL2,COL3 FROM TABLE WHERE ID IN ( " + string.Join(",", parms) + " ) ";
var resultList = new List<ReturnClass>();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var values = new ReturnClass();
values.Id = (int)reader["ID"];
values.Col1 = reader["COL1"].ToString();
values.Col2 = reader["COL2"].ToString();
resultList.Add(values);
}
}
return resultList;
}
}
}
public class ReturnClass
{
public int Id;
public string Col1;
public string Col2;
}
Try changing from:
db2Parameter.DB2Type = DB2Type.DynArray;
to:
db2Parameter.DB2Type = DB2Type.Integer;
This is based on the example given here

Entity Framework Get Table By Name

I am looking for ways to do LINQ on a table selected in runtime via string variable.
This is what I have so far using reflection:
private Entities ctx = new Entities();
public List<AtsPlatform> GetAtsPlatformByName(string atsPlatformName)
{
List<AtsPlatform> atsPlatform = null;
System.Reflection.PropertyInfo propertyInfo = ctx.GetType().GetProperty(atsPlatformName.ToLower());
var platform = propertyInfo.GetValue(ctx, null);
// it fails here highlighting "platform" with error that reads "Error 1 Could not find an implementation of the query pattern for source type 'System.Data.Objects.ObjectQuery'. 'Select' not found. Consider explicitly specifying the type of the range variable 'ats'."
atsPlatform = ((from ats in platform select new AtsPlatform { RequestNumber = ats.RequestNumber, NumberOfFail = ats.NumberOfFail, NumberOfFailWithCR = ats.NumberOfFailWithCR, NumberOfTestCase = ats.NumberOfTestCase }).ToList());
return atsPlatform;
}
In my model class, I have:
public class AtsPlatform
{
public string Name { get; set; }
public string RequestNumber { get; set; }
public Int32? NumberOfFail { get; set; }
public Int32? NumberOfTestCase { get; set; }
public Int32? NumberOfFailWithCR { get; set; }
}
In Database, I have the following tables: "ats1", "ats2", "ats3" .. "atsN" where each of them has the same entity fields as the properties defined in "AtsPlatform"
What I would like to do is simply:
List<AtsPlatform> a1 = GetAtsPlatformByName("ats1");
List<AtsPlatform> a2 = GetAtsPlatformByName("ats2");
List<AtsPlatform> aN = GetAtsPlatformByName("atsN");
I could use "switch" but this makes the code less expandable and requires update whenever new "ats(N+1)" gets created.
My 2 days of research lead me nowhere but back to ground zero. I'm quite stuck.
PLEASE HELP! Thanks!
Instead of reflection, how about using the SqlQuery function?
So
List<AtsPlatform> GetAtsPlatformByName(int index)
{
using (var ctx = new Entities())
{
return ctx.Database.SqlQuery<AtsPlatform>("SELECT * FROM dbo.ats" + index)
.ToList();
}
}
Also, there is no change tracking on the entities using the SqlQuery method on the Database object (which is ok in your case I suppose since the AtsPlatform class only contains primitive properties).
For changes tracking you will need to use the DbSet SqlQuery method, and may need to mix some reflection in.
Sorry for my late response as I wondered off trying out different solutions:
Solution #1: Master Table
As suggested by #Alexw, creating a Master Table works the best ONLY if you are allowed to change the design of the db. I'm currently working with the db owner to make this change. Due to dependencies, this change has to wait till next phase.
Meanwhile, I've created mock db to exercise this approach.
Solution #2: Raw Query
As Suggested by #Umair, raw query will do the job. I've created a class that handles raw sql query.
public class AtsRawQuery
{
private string ConnetionString = "";
public AtsRawQuery(string connectionString)
{
this.ConnetionString = connectionString;
}
public List<List<string>> Query(string queryString)
{
List<List<string>> results = null;
MySqlConnection conn = null;
MySqlDataReader rdr = null;
try
{
conn = new MySqlConnection(this.ConnetionString);
conn.Open();
MySqlCommand cmd = new MySqlCommand(queryString, conn);
rdr = cmd.ExecuteReader();
if (rdr.HasRows)
{
results = new List<List<string>>();
while (rdr.Read())
{
List<string> curr_result = new List<string>();
for (int columnIndex = 0; columnIndex <= rdr.FieldCount - 1; columnIndex++)
{
curr_result.Add(rdr.GetString(columnIndex));
}
results.Add(curr_result);
}
}
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
return null;
}
finally
{
if (rdr != null)
{
rdr.Close();
}
if (conn != null)
{
conn.Close();
}
}
return results;
}
}
This class returns a 2 dimension list for later consumption.
In my model class, I added a parser method:
public class AtsPlatform
{
public string Name { get; set; }
public string RequestNumber { get; set; }
public Int32? NumberOfFail { get; set; }
public Int32? NumberOfTestCase { get; set; }
public Int32? NumberOfFailWithCR { get; set; }
public void Parse(string name, string requestNumber, string numberOfFail, string numberOfTestCase, string numberOfFailWithCR)
{
Int32 temp;
this.Name = name;
this.RequestNumber = requestNumber;
this.NumberOfFail = (Int32.TryParse(numberOfFail, out temp)) ? Int32.Parse(numberOfFail) : 0;
this.NumberOfTestCase = (Int32.TryParse(numberOfTestCase, out temp)) ? Int32.Parse(numberOfTestCase) : 0;
this.NumberOfFailWithCR = (Int32.TryParse(numberOfFailWithCR, out temp)) ? Int32.Parse(numberOfFailWithCR) : 0;
}
}
Solution #2(b): Raw Query using ExecuteStoreCommand
public List<AtsPlatform> GetAtsPlatformByName(string atsPlatformName)
{
List<AtsPlatform> atsPlatforms = null;
string stm = String.Format("SELECT RequestNumber, NumberOfFail, NumberOfTestCase, NumberOfFailWithCR FROM {0}", atsPlatformName);
atsPlatforms = new List<AtsPlatform>();
foreach (AtsPlatform ats in ctx.ExecuteStoreQuery<AtsPlatform>(stm))
{
atsPlatforms.Add(ats);
}
return atsPlatforms;
}
Solution #3: Stored Procedure
I've created a stored procedure and here is the code:
DELIMITER $$
CREATE PROCEDURE `UnionAtsTables`()
BEGIN
DECLARE atsName VARCHAR(10);
DECLARE atsIndex INT;
SET atsIndex = 1;
SET #qry = '';
WHILE atsIndex > 0 DO
SET atsName =concat('ATS',atsIndex);
IF sf_is_table(atsName) = 1 THEN
Set #temp_qry = CONCAT('SELECT *, ''', atsName ,''' As TestPlatform FROM ', atsName, ' WHERE RequestNumber <> ''''' );
If #qry = '' THEN
SET #qry = #temp_qry;
ELSE
SET #qry = CONCAT(#qry, ' UNION ', #temp_qry);
END IF;
ELSE
SET atsIndex = -1;
END IF;
SET atsIndex = atsIndex + 1;
END WHILE;
DROP TABLE IF EXISTS ats_all;
SET #CreateTempTableQuery = CONCAT('CREATE TEMPORARY TABLE ats_all AS ', #qry ,'');
PREPARE stmt1 FROM #CreateTempTableQuery;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
ALTER TABLE ats_all DROP COLUMN ExecOrder;
ALTER TABLE ats_all ADD ExecOrder INT PRIMARY KEY AUTO_INCREMENT;
ALTER TABLE ats_all auto_increment = 0;
END
Here is the function I found online that checks if table exists in db.
DELIMITER $$
CREATE FUNCTION `sf_is_table`(`in_table` varchar(255)) RETURNS tinyint(4)
BEGIN
/**
* Check if table exists in database in use
*
* #name sf_is_table
* #author Shay Anderson 08.13 <http://www.shayanderson.com>
*
* #param in_table (table name to check)
* #return TINYINT (1 = table exists, 0 = table does not exist)
*/
# table exists flag
DECLARE is_table BOOLEAN DEFAULT FALSE;
# table count
DECLARE table_count INT DEFAULT 0;
# database name
SET #db = NULL;
# set database name
SELECT
DATABASE()
INTO
#db;
# check for valid database and table names
IF LENGTH(#db) > 0 AND LENGTH(in_table) > 0 THEN
# execute query to check if table exists in DB schema
SELECT COUNT(1) INTO table_count
FROM information_schema.`TABLES`
WHERE TABLE_SCHEMA = #db
AND TABLE_NAME = in_table;
# set if table exists
IF table_count > 0 THEN
SET is_table = TRUE;
END IF;
END IF;
RETURN is_table;
END
Conclusion:
Thank you everyone for your suggestions.
I decided to use Solution #2 since it does not cause as much impact to the db performance as Solution #3 and it does not require db redesign as Solution #1.
I don't think what you are doing will work like that. You should create an entity based on a single 'master' table, eg. Ats
Once you have done this you will have a property on your Entities class called Ats. You can now use this property to select the entities using a raw SQL query like this.
var atsName = "ats1";
using (var context = new Entities())
{
var blogs = context.Ats.SqlQuery(string.Format("SELECT * FROM {0}", atsName)).ToList();
}
Alternatively you could try this (I am assuming the property type is DBSet as you haven't specified it in the question)
var platform = propertyInfo.GetValue(ctx, null) as DBSet<Ats>;
atsPlatform = platform.Select(ats => new A new AtsPlatform { RequestNumber = ats.RequestNumber, NumberOfFail = ats.NumberOfFail, NumberOfFailWithCR = ats.NumberOfFailWithCR, NumberOfTestCase = ats.NumberOfTestCase }).ToList();
return atsPlatform;

Linq to Sql: How to quickly clear a table

To delete all the rows in a table, I am currently doing the following:
context.Entities.DeleteAllOnSubmit(context.Entities);
context.SubmitChanges();
However, this seems to be taking ages. Is there a faster way?
You could do a normal SQL truncate or delete command, using the DataContext.ExecuteCommand method:
context.ExecuteCommand("DELETE FROM Entity");
Or
context.ExecuteCommand("TRUNCATE TABLE Entity");
The way you are deleting is taking long because Linq to SQL generates a DELETE statement for each entity, there are other type-safe approaches to do batch deletes/updates, check the following articles:
Batch Updates and Deletes with LINQ to SQL
LINQ to SQL Extension: Batch Deletion with Lambda Expression
Unfortunately LINQ-to-SQL doesn't execute set based queries very well.
You would assume that
context.Entities.DeleteAllOnSubmit(context.Entities);
context.SubmitChanges();
will translate to something like
DELETE FROM [Entities]
but unfortunately it's more like
DELETE FROM [dbo].[Entities] WHERE ([EntitiesId] = #p0) AND ([Column1] = #p1) ...
DELETE FROM [dbo].[Entities] WHERE ([EntitiesId] = #p0) AND ([Column1] = #p1) ...
DELETE FROM [dbo].[Entities] WHERE ([EntitiesId] = #p0) AND ([Column1] = #p1) ...
You'll find the same when you try to do bulk update in LINQ-to-SQL. Any more than a few hundred rows at a time and it's simply going to be too slow.
If you need to do batch operations & you're using LINQ-to-SQL, you need to write stored procedures.
I like using an Extension Method, per the following:
public static class LinqExtension
{
public static void Truncate<TEntity>(this Table<TEntity> table) where TEntity : class
{
var rowType = table.GetType().GetGenericArguments()[0];
var tableName = table.Context.Mapping.GetTable(rowType).TableName;
var sqlCommand = String.Format("TRUNCATE TABLE {0}", tableName);
table.Context.ExecuteCommand(sqlCommand);
}
}
you could also use this:
Public void BorraFilasTabla()
{
using(basededatos db = new basededatos())
{
var ListaParaBorrar = db.Tabla.Tolist();
db.Tabla.RemoveRange(ListaParaBorrar);
}
}
The Below c# code is used to Insert/Update/Delete/DeleteAll on a database table using LINQ to SQL
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace PracticeApp
{
class PracticeApp
{
public void InsertRecord(string Name, string Dept) {
LinqToSQLDataContext LTDT = new LinqToSQLDataContext();
LINQTOSQL0 L0 = new LINQTOSQL0 { NAME = Name, DEPARTMENT = Dept };
LTDT.LINQTOSQL0s.InsertOnSubmit(L0);
LTDT.SubmitChanges();
}
public void UpdateRecord(int ID, string Name, string Dept)
{
LinqToSQLDataContext LTDT = new LinqToSQLDataContext();
LINQTOSQL0 L0 = (from item in LTDT.LINQTOSQL0s where item.ID == ID select item).FirstOrDefault();
L0.NAME = Name;
L0.DEPARTMENT = Dept;
LTDT.SubmitChanges();
}
public void DeleteRecord(int ID)
{
LinqToSQLDataContext LTDT = new LinqToSQLDataContext();
LINQTOSQL0 L0;
if (ID != 0)
{
L0 = (from item in LTDT.LINQTOSQL0s where item.ID == ID select item).FirstOrDefault();
LTDT.LINQTOSQL0s.DeleteOnSubmit(L0);
}
else
{
IEnumerable<LINQTOSQL0> Data = from item in LTDT.LINQTOSQL0s where item.ID !=0 select item;
LTDT.LINQTOSQL0s.DeleteAllOnSubmit(Data);
}
LTDT.SubmitChanges();
}
static void Main(string[] args) {
Console.Write("* Enter Comma Separated Values to Insert Records\n* To Delete a Record Enter 'Delete' or To Update the Record Enter 'Update' Then Enter the Values\n* Dont Pass ID While Inserting Record.\n* To Delete All Records Pass 0 as Parameter for Delete.\n");
var message = "Successfully Completed";
try
{
PracticeApp pa = new PracticeApp();
var enteredValue = Console.ReadLine();
if (Regex.Split(enteredValue, ",")[0] == "Delete")
{
Console.Write("Delete Operation in Progress...\n");
pa.DeleteRecord(Int32.Parse(Regex.Split(enteredValue, ",")[1]));
}
else if (Regex.Split(enteredValue, ",")[0] == "Update")
{
Console.Write("Update Operation in Progress...\n");
pa.UpdateRecord(Int32.Parse(Regex.Split(enteredValue, ",")[1]), Regex.Split(enteredValue, ",")[2], Regex.Split(enteredValue, ",")[3]);
}
else
{
Console.Write("Insert Operation in Progress...\n");
pa.InsertRecord(Regex.Split(enteredValue, ",")[0], Regex.Split(enteredValue, ",")[1]);
}
}
catch (Exception ex)
{
message = ex.ToString();
}
Console.Write(message);
Console.ReadLine();
}
}
}

Categories