how to get multiple result sets using linq - c#

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,
//...
};
}
}

Related

ExecuteNonQuery is not resolving parameters passed to it

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

C# VSTS WorkItem related count fields show zero even though I see related links from VSTS web app

I'm trying to pull some information from our VSTS hosted evironment that I need to present as report. What I observed that the API library is reporting there are no related items associated with that WorkItem even though I see related links from the VSTS web app.
Here is my code -
void Main()
{
string url = "https://[redacted].visualstudio.com";
string token = "[redacted]";
string project = "[redacted]";
string version = "[redacted]";
VssConnection conn = GetConnection(url, token);
WorkItemTrackingHttpClient witClient = conn.GetClient<WorkItemTrackingHttpClient>();
Wiql q = new Wiql();
q.Query = $"SELECT * FROM WorkItems WHERE [System.TeamProject] = '{project}' AND [System.Tags] CONTAINS '{version}' AND [System.WorkItemType] IN ('Product Backlog Item', 'Defect') ORDER BY [System.CreatedDate] desc";
var qi = witClient.QueryByWiqlAsync(q).Result;
var ids = qi.WorkItems.Select(x => x.Id);
var workitems = witClient.GetWorkItemsAsync(ids).Result.Select(r =>
{
return new
{
ItemId = r.Id,
ItemAssignedTo = r.Fields["System.AssignedTo"],
ItemCreatedBy = r.Fields["System.CreatedBy"],
ItemTitle = r.Fields["System.Title"],
ItemType = r.Fields["System.WorkItemType"],
State = r.Fields["System.State"],
ItemHasDescription = r.Fields.ContainsKey("System.Description") ? "Yes" : "No",
ItemHasAcceptanceCriteria = r.Fields.ContainsKey("Microsoft.VSTS.Common.AcceptanceCriteria") ? "Yes" : "No",
RelatedItems = r.Fields.ContainsKey("System.RelatedLinkCount") ? r.Fields["System.RelatedLinkCount"] : null //This line reports no related links,
Links = r.Links != null ? r.Links.Links : null //So does this line report null
};
});
workitems.Dump();
conn.Disconnect();
}
private static VssConnection GetConnection(string accountUri, string personalAccessToken)
{
var cred = new VssBasicCredential(string.Empty, personalAccessToken);
VssHttpMessageHandler vssHandler = new VssHttpMessageHandler(cred, VssClientHttpRequestSettings.Default.Clone());
return new VssConnection(
new Uri(accountUri),
vssHandler,
new DelegatingHandler[] { new SuppressHandler() });
}
public class SuppressHandler : DelegatingHandler
{
}
Also I'm getting these console logging which I'd like to avoid.
Web method running: [https://[redacted].visualstudio.com/_apis/wit/wiql] (POST)wiql[wit]
Is there a way I can remove these console logging?
Cannot figure out the problem based on your code.
However you can use below code sample to retrieve the work item information from VSTS, it works on my side:
To avoid getting these console logging, you can disable the Program output :
Right-click in the Output Window --> deselect the option Program output, then try it again.
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using System;
using System.Collections.Generic;
using System.Linq;
namespace QueryWorkitems0619
{
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("https://{account}.visualstudio.com");
string PAT = "TokenHere";
string project = "ProjectName";
VssBasicCredential credentials = new VssBasicCredential("", PAT);
//create a wiql object and build our query
Wiql wiql = new Wiql()
{
Query = "Select * " +
"From WorkItems " +
"Where [Work Item Type] IN ('Product Backlog Item', 'Task') " +
"And [System.TeamProject] = '" + project + "' " +
"And [System.State] <> 'Closed' " +
"And [System.RelatedLinkCount] > '0'" +
"Order By [State] Asc, [Changed Date] Desc"
};
//create instance of work item tracking http client
using (WorkItemTrackingHttpClient workItemTrackingHttpClient = new WorkItemTrackingHttpClient(uri, credentials))
{
//execute the query to get the list of work items in the results
WorkItemQueryResult workItemQueryResult = workItemTrackingHttpClient.QueryByWiqlAsync(wiql).Result;
//some error handling
if (workItemQueryResult.WorkItems.Count() != 0)
{
//need to get the list of our work item ids and put them into an array
List<int> list = new List<int>();
foreach (var item in workItemQueryResult.WorkItems)
{
list.Add(item.Id);
}
int[] arr = list.ToArray();
//build a list of the fields we want to see
string[] fields = new string[3];
fields[0] = "System.Id";
fields[1] = "System.Title";
fields[2] = "System.RelatedLinkCount";
//get work items for the ids found in query
var workItems = workItemTrackingHttpClient.GetWorkItemsAsync(arr, fields, workItemQueryResult.AsOf).Result;
Console.WriteLine("Query Results: {0} items found", workItems.Count);
//loop though work items and write to console
foreach (var workItem in workItems)
{
Console.WriteLine("ID:{0} Title:{1} RelatedLinkCount:{2}", workItem.Id, workItem.Fields["System.Title"], workItem.Fields["System.RelatedLinkCount"]);
}
Console.ReadLine();
}
}
}
}
}
I just had a similar problem, what you have to do is use the WorkItemExpand parameter of GetWorkItemAsync method like below:
var item = client.GetWorkItemAsync(c_projectName, id, null, null, WorkItemExpand.Relations).Result;
If not used the Relations property is null, which is quite misleading. If used it stores the proper information about related work.

UWP - Insert data from the server (json) into the database with certain conditions

I have an app that checks the data on the server (json) and the database. I want when the id on json is not the same as the id on the database, it will insert all data with unequal id into the database.
Code:
var sqlpath = System.IO.Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "ebookstore.db");
this.DataContextChanged += (s, e1) => { UpdateViewModel = DataContext as ViewModels.UpdateViewModel; };
string idDb = #"SELECT id FROM books where parent_folder_id = 2 and title like '%guru%'";
var IDdb = objConnUpdate.Prepare(idDb);
IDdb.Step();
iddb = IDdb[0].ToString();
IDDB = Convert.ToInt32(iddb.ToString());
ConnectionProfile connections = NetworkInformation.GetInternetConnectionProfile();
{
try
{
Downloading.IsOpen = true;
string urlPath1 = "https://.../fetch/k13G";
var httpClient1 = new HttpClient(new HttpClientHandler());
httpClient1.DefaultRequestHeaders.TryAddWithoutValidation("KIAT-API-KEY", "...*");
var values1 = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("halaman", "1"),
new KeyValuePair<string, string>("limit", "20"),
};
var response1 = await httpClient1.PostAsync(urlPath1, new FormUrlEncodedContent(values1));
response1.EnsureSuccessStatusCode();
string jsonText1 = await response1.Content.ReadAsStringAsync();
JsonObject jsonObject1 = JsonObject.Parse(jsonText1);
JsonArray jsonData1 = jsonObject1["data"].GetArray();
foreach (JsonValue groupValue in jsonData1)
{
JsonObject groupObject = groupValue.GetObject();
string ID = groupObject["id"].GetString();
BukuUpdate file1 = new BukuUpdate();
file1.ID = ID;
int intID = Convert.ToInt32(file1.ID);
if (intID != IDDB)
{
string jumlahidDb = #"SELECT COUNT(id) FROM books where parent_folder_id = 2 and id > " + IDDB + " and title like '%guru%'";
var jumlahIdDB = objConnUpdate.Prepare(jumlahidDb);
jumlahIdDB.Step();
if (jumlahiddb < jumlahbuku)
{;
if (nama == "Kelas_01_SD_")
{
DownloadBukuK2013G(url);
string K2013GUpdate = #"INSERT INTO books (id,title,folder_id,identifier,parent_folder_id) SELECT " + intID + ",'" + namaFile + ".pdf',34,'" + namaFile +
".pdf',2 WHERE not exists (select id AND title AND folder_id AND identifier AND parent_folder_id FROM books WHERE id=" + intID + " and title='" + namaFile +
".pdf' AND folder_id=34 and identifier='" + namaFile + ".pdf' and parent_folder_id=2)";
var K2013GQuery = objConnUpdate.Prepare(K2013GUpdate);
K2013GQuery.Step();
}
BukuUpdate.cs:
public class BukuUpdate
{
public string ID { get; set; }
}
Database:
I did not succeed in implementing it. How to handle it?
But if I use the code in my previous post, it only shows the first id in the database
You getting the first id by this code line iddb = IDdb[0].ToString(); for using. Actually the id list is saved in idDb variable.
I want if the id on json is not the same as the id on the database, then the data will be added to the database. If the same, then do nothing.
For this, you may just get all the ids from the database, and compare with the new income record from Json one by one. From your code snippet I'm not sure what's the Nuget package you're using for Sqlite, but the newest official tutorial using Microsoft.Data.SQLite package. I also strongly recommended you to use Microsoft.Data.SQLite package by following the official tutorial. By this way, the insert sample code may like this:
public sealed partial class MainPage : Page
{
private void btngettest_Click(object sender, RoutedEventArgs e)
{
List<String> ids = sqlhelp.GetData();
string idfromjson = "2";
foreach (string id in ids)
{
if (id != idfromjson)
{
//do insert operation
}
else
{
//do nothing
}
}
}
}
public class sqlhelp
{
public static void InitializeDatabase()
{
...
}
public static List<String> GetData()
{
List<String> entries = new List<string>();
using (SqliteConnection db =
new SqliteConnection("Filename=sqliteSample.db"))
{
db.Open();
SqliteCommand selectCommand = new SqliteCommand
("SELECT id from MyTable", db);
SqliteDataReader query = selectCommand.ExecuteReader();
while (query.Read())
{
entries.Add(query.GetString(0));
}
db.Close();
}
return entries;
}
Since your code snippet contains a lot of your own logic that I didn't give changes on your original code snippet. Please kindly reference my simple code which may be more clearly to know.

How do I sanitize a dynamic table name using Dapper?

I'm new to Dapper, and writing a query that will pull from a provided schema and table, along with using dynamic ordering and filtering.
Dapper make dynamic parameters very simple, however, I'm not sure how to do this with tables in the order by and where clauses. Here's my method below, and I see the issues with SQL injection:
public GridData GetGridData(string schema, string table, TableDataParameters tableDataParameters)
{
using (var dbConnection = VarConnection)
{
dbConnection.Open();
if (!this.TableExists(dbConnection, schema, table))
{
throw new ItemNotFoundException($"Could not locate table {schema}.{table}.");
}
string orderyByClause = string.Join(",", tableDataParameters.SortModel.Select(s => $"[{s.ColId}] {(s.Sort.ToLower() == "asc" ? "asc" : "desc")}"));
var parameters = new DynamicParameters();
string whereClause;
if (tableDataParameters.FilterModel == null || !tableDataParameters.FilterModel.Any())
{
whereClause = "1=1";
}
else
{
whereClause = string.Join(" AND ", tableDataParameters.FilterModel.Select((fm, i) =>
{
string whereParam = $"whereParam{i}";
parameters.Add(whereParam, fm.Filter);
if (fm.Operation == "startsWith")
{
return $"[{fm.Column}] LIKE #{whereParam} + '%'";
}
throw new InvalidOperationException($"Unsupported filter operation '{fm.Operation}'");
}));
}
var query = $"SELECT COUNT(1) [total] " +
$"FROM [{schema}].[{table}] " +
$"WHERE {whereClause} " +
$"SELECT * " +
$"FROM [{schema}].[{table}] " +
$"WHERE {whereClause} " +
$"ORDER BY {orderyByClause} " +
$"OFFSET {tableDataParameters.StartIndex.Value} ROWS " +
$"FETCH NEXT {tableDataParameters.StopIndex.Value - tableDataParameters.StartIndex.Value} ROWS ONLY";
int total = 0;
using (var reader = dbConnection.ExecuteReader(query, parameters))
{
// First batch, it's the count
if (reader.Read())
{
total = reader.GetInt32(0);
}
var gridColumns = new List<GridColumn>();
var gridRows = new List<string[]>();
if (reader.NextResult() && reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
string key = reader.GetName(i);
gridColumns.Add(new GridColumn(key, key, null, ""));
}
var items = new object[reader.FieldCount];
reader.GetValues(items);
gridRows.Add(items.Select(i => i.ToString()).ToArray());
}
while (reader.Read())
{
var items = new object[reader.FieldCount];
reader.GetValues(items);
gridRows.Add(items.Select(i => i.ToString()).ToArray());
}
return new GridData(tableDataParameters.StartIndex.Value, tableDataParameters.StopIndex.Value, total, gridRows.Count(), gridColumns.ToArray(), gridRows.ToArray());
}
}
}
Should I use something like DbCommandBuilder.QuoteIdentifier, https://msdn.microsoft.com/en-us/library/system.data.common.dbcommandbuilder.quoteidentifier(v=vs.110).aspx
in this case? That doesn't seem like it would help so much here.
Thanks!
Dynamic parameters is an oxymoron! Dapper makes parameters easy, but you can't paramaterize table and column names. This is a restriction of SQL, not dapper. If you really want to do this, you have to use dynamic sql and string methods, and you're on your own as regards SQL injection.
You will be happier and live longer if you don't do this. It's just a bad road. You're not adding much value, and you're potentially introducing a load of problems and limitations.
It looks like you're writing an app to browse a database. Good tools already exist for this!

C# FIxing Foreach loop with no values

I'm trying to execute the following code, where two columns full of game ids in my DB are matched, then three separate queries are executed to pull related information for the game ids that matched, and it's all packaged nicely in an e-mail.
foreach (var TempId in ResultGameIDCompare)
var command1 = "EXEC DB query";
var command2 = "EXEC DB query";
var command3 = "EXEC DB query";
var GameInfo = db2.Query(command1, (string)TempId.Game_ID);
var SystemInfo = db2.Query(command2, (string)TempId.Game_ID);
var EditionInfo = db2.Query(command3, (string)TempId.Game_ID);
foreach (var result in GameInfo)
{
foreach (var result2 in SystemInfo)
{
foreach (var result3 in EditionInfo)
{
var text = " <ul>"+
"<p>My game information is as follows:" +#result.Name+"</p>"+
" <strong>System:</strong> " #result2.System+
" <strong>Price:</strong> " +#result3.Price+
" <strong>Edition:</strong> " +#result3.Edition+
"<ul>";
WebMail.Send("Test#test.com",
"This is a test",
body : text,
isBodyHtml:true );
}
}
}
The code works but if one of my queries returns an empty row nothing is fed into the collection for that game id and understandably no e-mail is generated for that game id.
So I'm trying to re-work my code so an e-mail is still generated even if an empty row is returned for the SystemInfo or EditionInfo query(GameInfo will never return an empty row) but I'm having difficulty figuring out how to go about this. I've attempted modifying my code as follows:
var Blank = " ";
foreach (var TempId in ResultGameIDCompare)
var command1 = "EXEC DB query";
var command2 = "EXEC DB query";
var command3 = "EXEC DB query";
var GameInfo = db2.Query(command1, (string)TempId.Game_ID);
var SystemInfo = db2.Query(command2, (string)TempId.Game_ID);
var EditionInfo = db2.Query(command3, (string)TempId.Game_ID);
foreach (var result in GameInfo)
{
foreach (var result2 in SystemInfo.DefaultIfEmpty(Blank))
{
foreach (var result3 in EditionInfo.DefaultIfEmpty(Blank))
{
var text = " <ul>"+
"<p>My game information is as follows:" +#result.Name+"</p>"+
" <strong>System:</strong> " #result2.System+
" <strong>Price:</strong> " +#result3.Price+
" <strong>Edition:</strong> " +#result3.Edition+
"<ul>";
WebMail.Send("Test#test.com",
"This is a test",
body : text,
isBodyHtml:true );
}
}
}
But it returns error "string' does not contain a definition for 'System'" and I'm honestly not sure if this alteration will do what I think it will do. Return " " in positions no data was returned from the query.
foreach (var result in GameInfo)
{
StringBuilder sb = new StringBuilder();
sb.Append("<ul>");
sb.AppendFormat("<p>My game information is as follows: {0}</p>", #result.Name);
foreach (var result2 in SystemInfo)
{
sb.AppendFormat("<strong>System: {0}</strong>", #result2.System);
foreach (var result3 in EditionInfo)
{
sb.AppendFormat("<strong>Price: {0}</strong>", #result3.Price);
sb.AppendFormat("<strong>Edition: {0}</strong>", #result3.Edtion);
}
}
sb.Append("</ul>");
WebMail.Send("Test#test.com",
"This is a test",
body: sb.ToString(),
isBodyHtml: true);
}
Just move the creation of the string outside into the first loop, and add to it in the inner loops as you get more data. StringBuilder is a high performance string concatenation system in the System.Text namespace and avoids pricey + concatenations.
Your variable Blank needs to be an IEnumerable of the same type as your original collection.
If your SystemInfo is a collection of strings,
SystemInfo.DefaultIfEmpty(new[]{ Blank })
will do.

Categories

Resources