ExecuteNonQuery is not resolving parameters passed to it - c#

I am attempting to add the variable newRetentionLimit to a table in Microsoft SQL Server. I pass the value I want to insert into a parameter and then run ExecuteNonQuery. I get no errors back but the newRetentionLimit isn't placed into the table. I have debugged to make sure that newRetentionLimit isn't null and is an actual integer.
The problem appears to be that ExecuteNonQuery isn't retrieving the parameter value based on the name I put in the script. It appears its just trying to run the script with the parameter name. Anyone have any idea why?
if (request.SystemSettings.Any(s => s.SettingName.Equals("HISTORYRETENTIONDAYS")))
{
var entities = entityRepo.GetList();
var newRetentionLimit = request.SystemSettings.Find(setting => setting.SettingName.Equals("HISTORYRETENTIONDAYS")).SettingValue.ToInt();
var requestContext = new RequestContext();
var sqlParameter = new List<SqlParameter> {
SqlParameterMaker.MakeTypedValueParameter("#retentionValue",newRetentionLimit, SqlDbType.Int)
};
foreach (var entity in entities)
{
var sql = $#"ALTER TABLE [data].[t{entity.Name}] SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = [hist].[t{entity.Name}], HISTORY_RETENTION_PERIOD = #retentionValue DAYS));";
requestContext.DatabaseContext.ExecuteNonQuery(sql, sqlParameter);
}
}

I ended up finding a solution that still allows me to maintain the use of SqlParameter
if (request.SystemSettings.Any(s => s.SettingName.Equals("HISTORYRETENTIONDAYS")))
{
var entities = entityRepo.GetList();
var newRetentionLimit = request.SystemSettings.Find(setting => setting.SettingName.Equals("HISTORYRETENTIONDAYS")).SettingValue.ToInt();
var requestContext = new RequestContext();
foreach (var entity in entities)
{
var sqlParameters = new List<SqlParameter>{
new SqlParameter("#entityName", entity.Name),
new SqlParameter("#retentionPeriod", newRetentionLimit)
};
var sql = "EXEC('ALTER TABLE [data].[t' + #entityName + '] SET (SYSTEM_VERSIONING = ON (HISTORY_RETENTION_PERIOD = ' + #retentionPeriod + ' DAYS))');";
requestContext.DatabaseContext.ExecuteNonQuery(sql, sqlParameters.ToArray());
}
}

Related

C# entity framework fromsqlraw query with includes (in) instead of where clause

What is the correct syntax for varname to make this query work?
I can get it to work with a single variable like
string varname = "TOTAL_NORWAY"
However, if I want to have a few variables in there, I get an empty array returned:
string varname = "'TOTAL_NORWAY', 'TOTAL_SWEDEN'";
return await _Context.theDataModel.FromSqlRaw(#"
select data
from data_table
where Variable in ({0})
", varname).ToListAsync();
Remember that you can combine FromSqlRaw with Linq:
string varnames = new [] { "TOTAL_NORWAY", "TOTAL_SWEDEN" };
var query = _Context.theDataModel.FromSqlRaw(#"
select data
from data_table");
query = query.Where(x => varnames.Contains(x.Variable));
// Add more where clauses as needed
return await query.ToListAsync();
ErikEJ's post was very helpful. The solution is not so trivial for someone who doesn't dabble in EF Core regularly.
I also had an extra where clause to consider, and this was done like so for anyone else wondering.
var items = new int[] { 1, 2, 3 };
var parameters = new string[items.Length];
var sqlParameters = new List<SqlParameter>();
for (var i = 0; i < items.Length; i++)
{
parameters[i] = string.Format("#p{0}", i);
sqlParameters.Add(new SqlParameter(parameters[i], items[i]));
}
sqlParameters.Add(new SqlParameter("#userid", "userXYZ123"));
var rawCommand = string.Format("SELECT * from dbo.Shippers WHERE ShipperId IN ({0}) and userid = {1}", string.Join(", ", parameters), "#userid");
var shipperList = db.Set<ShipperSummary>()
.FromSqlRaw(rawCommand, sqlParameters.ToArray())
.ToList();

how to get multiple result sets using linq

I have a stored procedure with around 14 different result sets. How do I retrieve them all as by now I only get the first result set.
[HttpGet]
[Route("tire-tabel")]
public List<DeviationCalculation_Result> TireTabel(decimal presentWidth, decimal presentAspectRatio, string presentRimSize, int maxDeviation)
{
using (var context = new OminiTireEntities())
{
var result = context.Database.SqlQuery<DeviationCalculation_Result>(
"exec [Tabel].[DeviationCalculation] #PresentWidth = '" + presentWidth + "', " +
"#PresentAspectRatio= '" + presentAspectRatio + "', " +
"#PresentInches= '" + presentRimSize + "', " +
"#MaxDeviation= '" + maxDeviation + "'").ToList<DeviationCalculation_Result>();
return result;
}
}
Sample Code:
using (var db = new BloggingContext())
{
// If using Code First we need to make sure the model is built before we open the connection
// This isn't required for models created with the EF Designer
db.Database.Initialize(force: false);
// Create a SQL command to execute the sproc
var cmd = db.Database.Connection.CreateCommand();
cmd.CommandText = "[dbo].[GetAllBlogsAndPosts]";
try
{
db.Database.Connection.Open();
// Run the sproc
var reader = cmd.ExecuteReader();
// Read Blogs from the first result set
var blogs = ((IObjectContextAdapter)db)
.ObjectContext
.Translate<Blog>(reader, "Blogs", MergeOption.AppendOnly);
foreach (var item in blogs)
{
Console.WriteLine(item.Name);
}
// Move to second result set and read Posts
reader.NextResult();
var posts = ((IObjectContextAdapter)db)
.ObjectContext
.Translate<Post>(reader, "Posts", MergeOption.AppendOnly);
foreach (var item in posts)
{
Console.WriteLine(item.Title);
}
}
finally
{
db.Database.Connection.Close();
}
}
The Translate method accepts the reader that we received when we executed the procedure, an EntitySet name, and a MergeOption. The EntitySet name will be the same as the DbSet property on your derived context. The MergeOption enum controls how results are handled if the same entity already exists in memory.
Reference : https://msdn.microsoft.com/en-us/library/jj691402(v=vs.113).aspx
I also recommend to use Parameters instead of executing the queries as mentioned in the question as it can result in SQL injection
With Dapper it is super simple:
public DeviationCalculationResult Get(decimal presentWidth, decimal presentAspectRatio, string presentRimSize, int maxDeviation)
{
using (var context = new OminiTireEntities())
{
var reader = context.Database.Connection.QueryMultiple("[Tabel].[DeviationCalculation]",
new
{
PresentWidth = presentWidth,
PresentAspectRatio = presentAspectRatio,
PresentInches = presentRimSize,
MaxDeviation = maxDeviation
}, commandType: CommandType.StoredProcedure);
var first = reader.Read<First>().ToList().First();
var second = reader.Read<Second>().ToList().First();
var third = reader.Read<Third>().ToList().First();
//...and so on...
return new DeviationCalculationResult
{
First = first,
Second = second,
Third = third,
//...
};
}
}

C# retrieve stored procedure results

Procedure (modified):
alter procedure searchProgramUnitResult(
#id char(10)
)
as
begin
select id from table1 where id = #id
end
Sam procedure in the DBML Designer (after importing the procedure to the MVC project):
[global::System.Data.Linq.Mapping.FunctionAttribute(Name="dbo.searchProgramUnit")]
public ISingleResult<searchProgramUnitResult> searchProgramUnit([global::System.Data.Linq.Mapping.ParameterAttribute(DbType="VarChar(10)")] ref string id){
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())),id);
id = ((string)(result.GetParameterValue(0)));
return ((ISingleResult<searchProgramUnitResult>)(result.ReturnValue));
}
Question is, how do I retrieve the result set in another C# class?
public ??Data-type search (string id){
DataContextClass db = new DataContextClass();
??Datatype results = db.searchProgramUnit(id);
return results;
}
If you have mapped the stored procedure in your DbContext you can call it like that:
using (var context = new DataContextClass())
{
var courses = context.searchProgramUnit("1");
foreach (table1 cs in table1s)
Console.WriteLine(cs.Name);
}
another approach which works also with Code First:
using (var ctx = new DataContextClass())
{
var idParam = new SqlParameter
{
ParameterName = "id",
Value = "1"
};
var table1List = ctx.Database.SqlQuery<table1>("exec searchProgramUnitResult #id ", idParam).ToList<table1>();
foreach (table cs in table1List)
Console.WriteLine("Name: {0}", cs.Name);
}
table1 is your entity/class name!
Are you asking about the data type?
public List<string> search (string id){
DataContextClass db = new DataContextClass();
List<string> results = db.searchProgramUnit(id).ToList();
return results;
}

DataAdapter .Update does not update back table

my problem is very common, but I have not found any solution.
This is my code:
public async Task<QueryResult> RollbackQuery(ActionLog action)
{
var inputParameters = JsonConvert.DeserializeObject<Parameter[]>(action.Values);
var data = DeserailizeByteArrayToDataSet(action.RollBackData);
using (var structure = PrepareStructure(action.Query, action.Query.DataBase, inputParameters))
{
//_queryPlanner is the implementor for my interface
return await _queryPlanner.RollbackQuery(structure, data);
}
}
I need to load DataTable (from whereever) and replace data to database. This is my Rollback function. This function use a "CommandStructure" where I've incapsulated all SqlClient objects. PrepareStructure initialize all objects
//_dataLayer is an Helper for create System.Data.SqlClient objects
//ex: _dataLayer.CreateCommand(preSelect) => new SqlCommand(preSelect)
private CommandStructure PrepareStructure(string sql, string preSelect, DataBase db, IEnumerable<Parameter> inputParameters)
{
var parameters = inputParameters as IList<Parameter> ?? inputParameters.ToList();
var structure = new CommandStructure(_logger);
structure.Connection = _dataLayer.ConnectToDatabase(db);
structure.SqlCommand = _dataLayer.CreateCommand(sql);
structure.PreSelectCommand = _dataLayer.CreateCommand(preSelect);
structure.QueryParameters = _dataLayer.CreateParemeters(parameters);
structure.WhereParameters = _dataLayer.CreateParemeters(parameters.Where(p => p.IsWhereClause.HasValue && p.IsWhereClause.Value));
structure.CommandBuilder = _dataLayer.CreateCommandBuilder();
structure.DataAdapter = new SqlDataAdapter();
return structure;
}
So, my function uses SqlCommandBuilder and DataAdapter to operate on Database.
PreSelectCommand is like "Select * from Purchase where CustomerId = #id"
The table Purchase has one primaryKey on ID filed
public virtual async Task<QueryResult> RollbackQuery(CommandStructure cmd, DataTable oldData)
{
await cmd.OpenConnectionAsync();
int record = 0;
using (var cmdPre = cmd.PreSelectCommand as SqlCommand)
using (var dataAdapt = new SqlDataAdapter(cmdPre))
using (var cmdBuilder = new SqlCommandBuilder(dataAdapt))
{
dataAdapt.UpdateCommand = cmdBuilder.GetUpdateCommand();
dataAdapt.DeleteCommand = cmdBuilder.GetDeleteCommand();
dataAdapt.InsertCommand = cmdBuilder.GetInsertCommand();
using (var tbl = new DataTable(oldData.TableName))
{
dataAdapt.Fill(tbl);
dataAdapt.FillSchema(tbl, SchemaType.Source);
tbl.Merge(oldData);
foreach (DataRow row in tbl.Rows)
{
row.SetModified();
}
record = dataAdapt.Update(tbl);
}
}
return new QueryResult
{
RecordAffected = record
};
}
I Execute the code and I don't have any errors, but the data are not updated.
variable "record" contain the right number of modified (??) record, but..... on the table nothing
can someone help me?
EDIT 1:
With SQL Profiler I saw that no query is executed on DB. Only select query on .Fill(tbl) command.
EDIT 2:
Now I have made one change:
tbl.Merge(oldData) => tbl.Merge(oldData, true)
so I see perform the expected query but, with reversed parameters.
UPDATE Purchase SET price=123 where id=6 and price=22
instead of
UPDATE Purchase SET price=22 where id=6 and price=123

How to get multiple inserted identity values in Entity Framework

I am inserting values in to the table QueryList
[QueryID] [WorkItemID] [RaisedBy]
1 123 xyz
2 234 abc
where QueryID is an Identity column.
I am using a foreach loop and inserting more than one value at a time. My question is how to get all the newly inserted Identity values in Entity Framework 3.5
This is my code
using (TransactionScope currentScope = new TransactionScope())
{
Query newQuery = new Query();
foreach (long workItemId in workItemID)
{
newQuery = new Query();
...
currentScope.Complete();
success = true;
}
}
entityCollection.SaveChanges(true);
int QueryID = newQuery.QueryID; //It gives me last 1 Identity value
You have to track each newly created Query object separately. I suggest using a List<Query> for simplicity:
using (TransactionScope curentScope = new TransactionScope())
{
List<Query> newQueries = new List<Query>();
Query newQuery = new Query();
newQueries.Add(newQuery);
foreach (long workItemId in workItemID)
{
newQuery = new Query();
newQueries.Add(newQuery);
...
curentScope.Complete();
success = true;
}
}
entityCollection.SaveChanges(true);
var queryIDs = newQueries.Select(q => q.QueryID);
Side note: In your code sample you created a Query object outside of the for-loop, but didn't use it at all. This may just be because it's just a sample, but if you use it or insert it in your data context, don't create it.

Categories

Resources