I have a stored procedure in SQL Server that returns the record from the database matching the ID I pass into it.
But when I call that stored procedure from my ASP.NET Core 6 project, I don't get the results I should.
The C# code looks like this:
private List<HeaderModel> getHeaderNames(List<TagIDModel> allID)
{
var allHeaders = new List<HeaderModel>();
for (int i = 0; i < counter; i++)
{
var header = _context.Translation.FromSqlRaw("EXEC getHeader #key = {0}", allID[i].TagID).ToList();
allHeaders.Add(new HeaderModel
{
HeaderName = header[0].EnUs
});
}
return removeDoubles(allHeaders);
}
In this case the allID collection has the following values:
1, 3 4, 6 & 7
They are passed one by one into the stored procedure which looks like this:
ALTER PROCEDURE [dbo].[getHeader]
#key int
AS
SELECT Translation.id, [Key], [EN-US], [NL-NL], [CN-ZH]
FROM Translation
JOIN ParameterGroup ON Translation.id = ParameterGroup.ParameterGroupName
JOIN TagData ON TagData.id=ParameterGroup.id
WHERE TagData.id = #key
But the result only match half the time, yet they work perfectly in SSMS 2018.
The result from the stored procedure should be:
id: 5
Key: 7h48f
EN-US: Header-1
But instead I receive this:
id: 5
Key: jsoej
EN-US: Mixing
The ID is correct, but for an unknown reason the other values are incorrect.
Related
I'm using asp.net to process a SQL query that returns a column from some table. Normally what I'd do is set a variable equal to the stored procedure function call and add .ToArray() at the end, which is what I want to do here but I'm getting an error message int does not contain a definition for toarray...
I'm confused because I followed the same syntax that I used in another part of the program for a similar thing. It worked fine before but I can't figure out why it wants to fight with me now.
Here's my SQL:
IF OBJECT_ID('#temp') IS NOT NULL
BEGIN
DROP TABLE #temp
END
--Create temp table to store data
CREATE TABLE #temp
(
EventID nvarchar(50),
RunDate date,
SectionCode nvarchar(50),
SectionMapCode nvarchar(50),
DispSort int,
Capacity int,
Attendance int,
PctCap int,
HeatColor nvarchar(50)
)
DECLARE #runDate date = GETDATE()
INSERT #temp Exec GamedayReporting.dbo.uspGetEventKillSheetDetailedReport #EventID, #runDate;
select Capacity from #temp;
This returns exactly what I want in SQL but when I call it in my Controller I get the error I posted above.
Here's my C# code:
public ActionResult Dropdown()
{
// add your code after post method is done
var selectedOption = Request["eventId"];
var date = DateTime.Today;
var myQuery = db.uspGetEventKillSheetDetailedReport(selectedOption, date).ToArray();
ViewData["query"] = myQuery;
System.Diagnostics.Debug.WriteLine(myQuery);
TempData["option"] = selectedOption;
return RedirectToAction("Map");
}
public ActionResult Map()
{
var secAttendance = db.uspGetSectionAttendance("option").ToArray();
var secCapacity = db.uspGetSecCapacity("option");
var secMapCode = db.uspGetSectionMapCode("option");
}
public JsonResult GetDropdownList()
{
var ids = db.uspGetAllEventIds().ToArray();
ViewData["ids"] = db.uspGetAllEventIds().ToArray();
return Json(new { data = ids }, JsonRequestBehavior.AllowGet);
}
So Dropdown() and GetDropdownList() work fine, but I'm getting the problem with Map().
Basically I want to take the column returned from my SP and store it into an array but it won't let me. Anybody able to help me work through this?
Update
I changed .ToArray() to .toString().toArray(), which got me past the compiler error, but upon logging it into the console I found it was returning char instead of string. So I changed the whole line to
string secAttendance = new string(db.uspGetSectionAttendance("option").ToString().ToArray());
and output the result into the console and found it returns 0.
0 comes from the Return Value in SQL, which I don't understand. It will fetch the correct column but will not send the correct data to ASP.
Here's a screenshot of the output from my SQL:
The error is correct. There must be only one row, one column value in the output. You are selecting
select Capacity from #temp;
which returns a single value as an int.
It cannot be directly cast into an array.
Instead, if you want an array, you can create a blank
Array<int> a = new Array<int>[1]
and then push this output to that array.
I have a field where i'm counting the total number of subscriptions to an user. Right now i update this field like this:
using (var context = new AppDbContext())
{
var foundEntry = context.Users.Find(id);
if (foundEntry != null)
{
foundEntry.TotalSubscriptions = foundEntry.TotalSubscriptions + 1;
context.Entry(foundEntry).CurrentValues.SetValues(foundEntry);
var result = context.SaveChanges();
}
}
But this way i have to make 2 queries to the db. One to get the current total count and one to update the value.
Is there a way to do this with only one query to the db with entity framework?
I wanted to try something like this:
var user = new User() { Id = userId, TotalSubscriptions = currentS + 1 };
db.Users.Attach(user);
db.Entry(user).Property(x => x.TotalSubscriptions).IsModified = true;
but the problem i'm facing is that i would have to get the current count first but don't know how to do in in a single query. I think that 1 query for this is possible with a raw SQL statement/query.
I'm trying to archive something like this: https://stackoverflow.com/a/2762856/1286942 but with entity framework.
UPDATE
I also tried something like this:
var user = new User() { Id = userId };
db.Users.Attach(user);
user.TotalSubscriptions = context.Entry(user)
.Property(u => u.TotalSubscriptions).OriginalValue + 1;
db.Entry(user).Property(x => x.TotalSubscriptions).IsModified = true;
But the problem is that the .OriginalValue and the .OriginalValue always return 0. So the TotalSubscriptions field is always updated to -1 or 1.
UPDATE #2
Right now i see only this as an option:
var numberOfUpdatedRows = context.Database
.ExecuteSqlCommand("UPDATE dbo.Users
SET TotalSubscriptions = TotalSubscriptions + 1
WHERE id = " + id + "");
The basic approach for this type of code is to use a stored procedure for your SQL code, and pass in a variable value to the procedure. Here is a possible solution for your problem.
CREATE PROCEDURE AdjustSubscriptions
#userid int,
#adjustment int
AS
BEGIN
DECLARE #subs int
SET NOCOUNT ON
UPDATE UserInfo
SET #subs = currentS + #adjustment, currentS = #subs
WHERE userID = #userid
SELECT #subs AS SubscriptionCount
END
This will SELECT the currentS value into a variable, adjust it as specified by the value in #adjustment, (Adding three subscriptions? Pass a three. Removing three subscriptions? Pass a negative three) then UPDATE the value to the saved row. Finally, a row containing only the adjusted subscriptionCount value is returned.
The stored procedure could be rewritten to return the value in an OUTPUT variable (if implemented correctly, you will simply get the value back into a variable instead of having to deal with a recordset), or even use the statement RETURN #subs at the end of the procedure to return the adjusted value as a return value. I highly recommend that you do NOT use the RETURN #subs method, since it breaks on a return of a string or a value outside of smallint.
I leave it to you to implement the entity framework code to handle the stored procedure. I don't believe that you can do the same direct manipulation of the value using one call within the entity framework, but I'm perfectly willing to be shown otherwise.
Happy Coding.
I have a stored procedure which returns maxNum. I can see the return value in the debugger: maxNum[0] has the value. I saw it in the debugger.
IEnumerable<int?> maxNum = DB.uspGetNextTestNumber();
How to read the value from it.
The above code is calling a stored procedure from ADO.NET Entity model.
ASP.NET MVC. VS2013. Windows 8.1 64 bit.
My stored procedure looks like this:
CREATE PROCEDURE [dbo].[uspGetNextTestNumber]
AS
SET NOCOUNT ON;
SELECT MAX(testnumber) + 1 as NextNumber
FROM [ProductTesting].[dbo].[PtTest] AS sp
GO
IEnumerable maxNum = DB.uspGetNextTestNumber();
List<int?> lst = maxNum.ToList<int?>();
foreach (var item in lst)
{
if (item.HasValue)
{
string str = item.Value.ToString();
// str has the value
}
}
Easy when using System.Linq:
int? number = DB.uspGetNextTestNumber().FirstOrDefault();
I am using C# in Visual Studio 2013 and SQL Server 2012.
When the user of my ASP.NET web form enters a newline-separated list of codes and clicks submit, the code behind should read the values, concatenate them into a comma-delimited string, and pass the string to a method that calls a stored proc. The stored proc parses out the values and sets the active field to 1 on each record with a matching code.
The Product table is defined as:
id (PK, int, not null),
name (varchar(20), not null),
code (varchar(20), null),
active (bit, not null)
The Product table contains the following five records:
id name code active
-- ---- ---- ------
1 Product 1 AAA 0
2 Product 2 BBB 0
3 Product 3 CCC 0
4 Product 4 DDD 0
5 Product 5 EEE 0
I created the following stored proc:
CREATE PROCEDURE [dbo].[MarkListAsActive]
#codeList varchar(MAX)
AS
UPDATE
dbo.Product
SET
active = 1
WHERE
code IN (SELECT val FROM dbo.f_split(#codeList, ','))
dbo.f_split handles parsing the comma-delimited string. I copied it from this post: https://stackoverflow.com/a/17481595/2677169
If I execute the stored proc in SQL Server Management Studio, all five records get updated (as expected).
DECLARE #return_value int
EXEC #return_value = [dbo].[MarkListAsActive]
#codeList = N'AAA,BBB,CCC,DDD,EEE'
SELECT 'Return Value' = #return_value
GO
However, if I call the stored proc from the code behind of my .aspx page, only the last item in the list gets marked as active.
protected void SubmitButton_Click(object sender, EventArgs e)
{
string[] codeArray;
char separator = '\n';
OutputLabel.Text = "";
codeArray = ProductCodeTextBox.Text.Split(separator);
OutputLabel.Text += "The products with the following codes were marked as active:<br />";
string codes = "";
// TODO: Replace with regex that changes newlines to commas
for (int i = 0; i < codeArray.Length; i++)
{
codes += codeArray[i] + ",";
OutputLabel.Text += codeArray[i] + "<br />";
}
codes = codes.Substring(0, codes.Length - 1);
Utilities.Log(codes);
DataAccess.MarkListAsActive(codes);
}
public static void MarkListAsActive(string codeList)
{
try
{
string connectionString = ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand("[dbo].[MarkListAsActive]", conn)
{
CommandType = CommandType.StoredProcedure
})
{
conn.Open();
command.Parameters.Add("#codeList", codeList);
command.ExecuteNonQuery();
conn.Close();
}
}
catch (Exception ex)
{
Utilities.Log(String.Format("Error in MarkListAsActive: {0}\n{1}", ex.Message, ex.StackTrace));
}
return;
}
Note that I verified that the string being passed to MarkListAsActive() is correct.
Another approach: I tried looping through the codeArray and calling MarkListAsActive() for each item. Even this brute force (and inefficient) approach only updated the last item in the array.
Yet another aproach: I also tried a table valued parameter, but it too only updated the record corresponding to the last item in the input.
Thank you in advance for your help.
Change the IN clause to (SELECT val FROM dbo.f_split(#codeList, ',') where val is not null).
If that doesn't work follow these steps:
(You should know how to debug something like this anyway.)
First, use SQL Profiler to see what exactly is submitted to the database. See here for how to do that - its not as bad as it looks and is invaluable. If not what is expected, you have a problem on the front end.
Second, if the call to the database looks good then take the parameter you see in SQL Profiler and execute dbo.f_split() using it to see if you get back what you think you should.
Last, change your UPDATE statement into a SELECT statement and run it with what you did in the second step and see if you get back something that looks correct.
One of those steps will not return what is expected and that will lead you to the problem.
Your where clause is incorrect. You cannot do
... code in (select val ...
You need to join the result from the f_split function to the table.
For example:
UPDATE p
SET p.active = 1
FROM dbo.Product p inner join dbo.f_split(#codeList, ',') f
WHERE p.code = f.val;
The goal
Count the results returned from a stored procedure.
The problem
I have the following code on my ProductsController:
[HttpGet]
public ActionResult DailyOffers()
{
var productsList = Products.BuildOffersList();
ViewBag.Title = String.Format("Ofertas de hoje ({0:dd/MM/yyyy})",
DateTime.Now);
ViewBag.CategoryProductsQuantity = ??;
ViewBag.CurrentCategory = "Daily-Offers";
return View(productsList);
}
As you can see, there is a builder on this method. This builder returns the Stored Procedure result. And I want to count the number of results that this procedure returns.
What I'm thinking about
Maybe this?:
ViewBag.CategoryProductsQuantity = productsList.Count;
Technical details
I'm using C#.NET + MySql + Entity Framework 5 + Razor Engine.
Assuming that Products.BuildOffersList(); does in fact return a List (and not an IEnumerable/IQueryable) then what you've suggested should be fine and won't result in multiple enumerations.
ViewBag.CategoryProductsQuantity = productsList.Count();
Use parameter "output" on Stored Procedure and create parameter "out" on your method.
#ROWCOUNT INT OUTPUT
SELECT
FROM
WHERE
SET #ROWCOUNT = ##ROWCOUNT