Been trying to insert a date, along with some other string data, into SQL via C#, as such:
OperationResponse ICommon.addUser(userRequest request)
{
int? result = 0;
Guid countryGUID;
string requestParent = request.parent;
string requestName = request.name;
string requestSchool = request.school;
int requestGender = request.gender;
DateTime dateTimeDOB = DateTime.ParseExact(request.DOB, "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture);
string requestEmail = request.email;
string requestMobile = request.mobile;
Guid.TryParse(request.countryID, out countryGUID);
string requestInstitution = request.institution;
try
{
result = s2u.AddUser(request.parent, request.name, request.school, request.gender, dateTimeDOB, request.email, request.mobile, countryGUID, request.institution);
}
catch (Exception ex)
{
if (isDebug() == true)
{
return new OperationResponse(ex.Message);
}
else
{
return new OperationResponse("Error: Database inaccessible.");
}
}
if (result == 1)
{
return new OperationResponse();
}
else
{
return new OperationResponse("Error: User could not be added.");
}
}
However whenever I try to run it in Fiddler it's been unsuccessful thus far, since the result always ends up being -1.
I suspect this may have something to do with the DOB not being inserted correctly, but I can't be sure. The request.DOB that I'm trying to parse is a string, the DOB column in the SQL table has the date datatype, and I'm trying to pass in dates in the format of "yyyy-MM-dd", but on the C# side I have to pass in the time too, being 12.00.00 AM by default. I don't know if this is causing problems. Any help is appreciated, and I'll provide further details if needed.
EDIT: The AddUser stored procedure is as such:
ALTER PROCEDURE [dbo].[AddUser]
-- Add the parameters for the stored procedure here
#parent nvarchar(300),
#name nvarchar(300),
#school nvarchar(500),
#gender int,
#DOB datetime2(7),
#email nvarchar(500),
#mobile nvarchar(20),
#countryID uniqueidentifier,
#institution nvarchar(500)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
INSERT INTO [dbo].[Users] ([parent], [name], [school], [gender], [DOB], [email], [mobile], [countryID], [institution])
VALUES (#parent, #name, #school, #gender, #DOB, #email, #mobile, #countryID, #institution);
END
Check your string date time is in correct format or not. I believe it's not in correct format.
Moreover, why didn't you use the following method?
DateTime dt = Convert.ToDateTime(dateTime);
With EF, datetime is mostly handle as a datetime2 type, so it won't make much difference or cause any trouble.
In addition, write transaction handles in stored procedure with try & catch and try to find out if sp is giving any problems or not.
Related
I have been trying to modify the accepted answer here which seems to leverage RelationalDataReader in order to get back 2 OUTPUT parameters for some calculated dates from an API that the Product Owners wished to be as much database driven as possible.
I have a stored procedure that calculates begin and end dates from the SQL queries I have stored in a table so that all they have to to to add another date range is to add it to the table and the rest will fall into place on the UI and the rest of the pipeline.
The first part is the same for the RDFacadeExtensions class but I thought that I could simply add more parameters and get them back, but I seem to be missing something.
var ID = new SqlParameter("ID", 5)
{
Direction = ParameterDirection.Input,
DbType = DbType.Int32,
Size = 500
};
var _beginDate = new SqlParameter("BeginDate", "")
{
Direction = ParameterDirection.Output,
DbType = DbType.String,
Size = 500
};
var _endDate = new SqlParameter("EndDate", "")
{
Direction = ParameterDirection.Output,
DbType = DbType.String,
Size = 500
};
This all seems normal and then I do the following:
string begin;
string end;
var sql = $"exec [dbo].[GetDateRange] #ID, #BeginDate OUTPUT, #EndDate OUTPUT";
using (var dr = context.Database.ExecuteSqlQuery(sql, ID, _beginDate, _endDate))
{
while (dr.DbDataReader.Read())
{
var thing = dr.DbDataReader[0].ToString();
}
dr.DbDataReader.Close();
begin = _beginDate.Value.ToString();
end = _endDate.Value.ToString();
}
Before the end of the using I briefly get the first date but I never seem to be able to get the second date. I also have empty strings once the datareader is closed.
If it matters, my stored procedure is simply this:
#ID INT,
#BeginDate DATE OUTPUT,
#EndDate DATE OUTPUT
BEGIN
SET NOCOUNT ON;
DECLARE #Query1 NVARCHAR(1000)
DECLARE #Query2 NVARCHAR(1000)
SELECT #Query1 = BeginDate,
#Query2 = EndDate
FROM DateRange
WHERE ID = #ID
DECLARE #ParmDefinition1 NVARCHAR(50) = N'#BeginDateOUT DATE OUTPUT';
DECLARE #ParmDefinition2 NVARCHAR(50) = N'#EndDateOUT DATE OUTPUT';
EXEC sp_executesql #Query1, #ParmDefinition1, #BeginDateOUT = #BeginDate OUTPUT
EXEC sp_executesql #Query2, #ParmDefinition2, #EndDateOUT = #EndDate OUTPUT
END
Or perhaps my stored procedure is the issue. I get one date but can only hold it for a short time. The issue with sp_executesql is that it has to go to an OUTPUT parameter and I can't assign the result to anything but an INT variable. I need two dates from the stored code in the table.
If there is a way to do that and only return what essentially amounts to a small table result that would be ideal.
Thanks you any that can point me in the right direction.
So, at least in ADO, if you don't close your data reader you will never get back your output parameters.
In your sample this should suffice your needs:
string begin;
string end;
var sql = $"exec [dbo].[GetDateRange] #ID, #BeginDate OUTPUT, #EndDate OUTPUT";
using (var dr = context.Database.ExecuteSqlQuery(sql, ID, _beginDate, _endDate))
{
while (dr.DbDataReader.Read())
{
var thing = dr.DbDataReader[0].ToString();
}
dr.DbDataReader.Close();
begin = _beginDate.Value.ToString();
end = _endDate.Value.ToString();
}
However there is one interesting solution mentioned in here which seems to tackle exactly your pain point.
So it would appear I don't need to use sp_executesql nor do I require any OUT parameters.
So I changed the stored procedure to this and minor change to the underlying queries held in the table to remove the select within them to basically allow the construction of the sql within the stored procedure.
ALTER PROCEDURE [dbo].[GetDateRange]
#ID INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Query1 NVARCHAR(1000)
DECLARE #Query2 NVARCHAR(1000)
SELECT #Query1 = BeginDate, #Query2 = EndDate FROM DateRange WHERE ID = #ID
DECLARE #Test NVARCHAR(1000) = 'SELECT ' + #Query1 + ' AS BeginDate, ' + #Query2 + ' AS EndDate'
EXECUTE (#Test)
END
Then I was able to do the following with the existing code:
var ID = new SqlParameter("ID", 1)
{
Direction = ParameterDirection.Input,
DbType = DbType.Int32,
Size = 500
};
DataTable dates = new DataTable();
var sql = $"exec [dbo].[GetDateRange] #ID";
using (var dr = context.Database.ExecuteSqlQuery(sql, ID))
{
dates.Load(dr.DbDataReader);
}
For some reason my stored procedure is executed without any error from the code-behind in C# but it is not deleting anything at all that the stored procedure has written. I have all the correct parameters and everything. I ran the query from SQL Server with all the same parameters from the C# code and it works perfectly. I don't get why it works when I run from SQL Server but it doesn't work when I run it from my C# code in Visual Studio.
Here is my C# code that is passing the data through to the stored procedure.
string reportType = "PostClaim";
string GRNBRs = "925','926','927";
string PUNBRs = "100','100','100";
string beginningDates = "20120401";
string endDates= "20120430";
try
{
conn = new SqlConnection(ConnectionInfo);
conn.Open();
SqlDataAdapter da = new SqlDataAdapter("RemoveReport", conn);
da.SelectCommand.CommandType = CommandType.StoredProcedure;
da.SelectCommand.Parameters.AddWithValue("#ReportType", reportType);
da.SelectCommand.Parameters.AddWithValue("#GRNBR", GRNBRs);
da.SelectCommand.Parameters.AddWithValue("#PUNBR", PUNBRs);
da.SelectCommand.Parameters.AddWithValue("#DATE1", beginningDates);
da.SelectCommand.Parameters.AddWithValue("#DATE2", endDates);
da.SelectCommand.CommandTimeout = 360;
}
catch (SqlException ex)
{
//something went wrong
throw ex;
}
finally
{
if (conn.State == ConnectionState.Open)
conn.Close();
}
Here is my stored procedure. It's executing with dynamic SQL text.
ALTER PROCEDURE [dbo].[RemoveReport] (
#ReportType NVARCHAR(20),
#GRNBR VARCHAR(4000),
#PUNBR VARCHAR(4000),
#DATE1 DATETIME,
#DATE2 DATETIME
)
AS
DECLARE #SQLTEXT VARCHAR(4000)
BEGIN
SET #SQLTEXT = 'DELETE FROM TestingTable
WHERE Report='''+#ReportType+''' AND
PUNBR IN ('''+#PUNBR+''') AND
[Group] IN ('''+#GRNBR+''') AND
StartedAt BETWEEN '''+CONVERT(VARCHAR(10),#DATE1,121)+'''
AND '''+CONVERT(VARCHAR(10),#DATE2,121)+''''
PRINT #SQLTEXT <---I'll print this out to show you what exactly it is executing.
EXECUTE (#SQLTEXT)
END
Here is what the PRINT #SQLTEXT is running:
DELETE FROM MonthlyReportSchedule
WHERE Report='PostClaim' AND
PUNBR IN ('100','100','100') AND
[Group] IN ('925','926','927') AND
StartedAt BETWEEN '2012-04-01' AND '2012-04-30'
When I actually go into SQL Server to run this query, it works perfectly. But why does it not work on when executed from the C# code. Any help?
Avoid concatenating parameters to your sql, use parameterised query,
Try this...
Just noticed that you have some comma delimited lists in params.....
ALTER PROCEDURE [dbo].[RemoveReport]
#ReportType NVARCHAR(20),
#GRNBR VARCHAR(4000),
#PUNBR VARCHAR(4000),
#DATE1 DATETIME,
#DATE2 DATETIME
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQLTEXT NVARCHAR(MAX);
Declare #GRNBR_xml xml,#PUNBR_xml xml;
SET #GRNBR_xml = N'<root><r>' + replace(#GRNBR, ',','</r><r>') + '</r></root>';
SET #PUNBR_xml = N'<root><r>' + replace(#PUNBR, ',','</r><r>') + '</r></root>';
SET #SQLTEXT = N'DELETE FROM TestingTable
WHERE Report = #ReportType
AND PUNBR IN (select r.value(''.'',''varchar(max)'') as item
from #PUNBR_xml.nodes(''//root/r'') as records(r))
AND [Group] IN (select r.value(''.'',''varchar(max)'') as item
from #GRNBR_xml.nodes(''//root/r'') as records(r))
AND StartedAt BETWEEN #DATE1 AND #DATE2'
EXECUTE sp_executesql #SQLTEXT
,N'#ReportType NVARCHAR(20) , #GRNBR_xml xml,
#PUNBR_xml xml,#DATE1 DATETIME,#DATE2 DATETIME'
,#ReportType
,#GRNBR_xml
,#PUNBR_xml
,#DATE1
,#DATE2
END
Note
Make sure you pass the comma delimited list as 925,926,927 and not as '925','926','927'
Try adding this line in order to be executed
da.SelectCommand.ExecuteNonQuery();
This will execute a call to your stored procedure.
good luck
I am trying to call a stored procedure using C# EF6 to bring back data. I have tried to run the stored procedure in SQL management studio and it seems to work fine, however when I try to run it in my application I get an error saying "Must declare the scalar variable "#devID"
Here is part of my method in my application calling the stored procedure
public IHttpActionResult GetMetrics(int deviceID, string attribute, string startDate)
{
if (deviceID == 0)
{
return NotFound();
}
var metrics = db.Database.SqlQuery<Metrics>("GetMetrics #devID, #MetricType, #startTime", deviceID, attribute, startDate).ToList();
and here is my stored procedure:
ALTER PROCEDURE [dbo].[GetMetrics]
-- Add the parameters for the stored procedure here
#devID int,
#MetricType nvarchar(20),
#startTime nvarchar(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT *
FROM dbMetrics
WHERE deviceID = #devID and MetricType = #MetricType and timeStamp >= #startTime
ORDER BY timeStamp
END
As per the documentation, if you want to use named parameters, you need to pass SqlParameter objects like this:
var metrics = db.Database.SqlQuery<Metrics>("GetMetrics #devID, #MetricType, #startTime",
new SqlParameter("devID", deviceID),
new SqlParameter("MetricType", attribute),
new SqlParameter("startTime", startDate)
).ToList();
I'm trying to compare a C# DateTime with a SQL-server DateTime in a Stored Procedure but it keeps giving me convert-errors.
At first someone else made the Oracle function for this:
'Select blabla from bla WHERE (TO_DATE (''' + cast(#dateEnd as varchar(50)) + ''',''yyyy/mm/dd'') >= SPOT_ENDDAT)
And I'm trying to change this to SQL but in SQL you don't have the TO_DATE function.
Any ideas? Or should I make the changes at the level of my .net program itself? If yes, what should I do?
EDIT:
Calling my function Stored Procedure with this parameter :
DateTime EndDate = DateTime.Today;
ParamList.Add(new <class>.Parameter("EndDate", ParameterDirection.Input, EndDate, DbType.Date));
Stored Procedure:
ALTER PROCEDURE dbo.uspGetValues
#EndDate = null;
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL as NVARCHAR(4000)
Set #SQL = 'SELECT * FROM T_SPOTSHOP_DATA WHERE SPOT_ENDDATE IS NOT NULL'
if(#EndDate is not null)
Set #SQL = #SQL + 'AND (' + #EndDate +' <= SPOT_ENDDATE'
EXEC(#SQL)
Edit Solution:
For those who have the same problem I fixed it the other-way around. In C# I would used :
DateTime EndDate = DateTime.Today.toString(yyyy-MM-dd);
ParamList.Add(new <class>.Parameter("EndDate", ParameterDirection.Input, EndDate, DbType.Date));
and I catch it up in my stored procedure as:
EndDate Varchar(50)
SET #SQL = #SQL + 'WHERE CONVERT(DATETIME, '''+ #EndDate +''', 121) >= SPOT_ENDDATE
It's a quite ugly way to do it but it works. Hopes it helps you guys!
try this:
Select
blabla
from
bla
WHERE CAST(#dateEnd AS DATETIME) >= CAST(SPOT_ENDDAT AS DATETIME)
You can perform the conversion in T-SQL using CONVERT, but I wouldn't.
I would strongly recommend avoiding string conversions as far as possible. Just use parameterized SQL, and specify the parameter as a DateTime:
// Assuming dateEnd is a DateTime variable
string sql = "SELECT blabla FROM bla WHERE #dateEnd >= SPOT_ENDDAT";
using (var command = new SqlCommand(conn, sql))
{
command.Parameters.Add("#dateEnd", SqlDbType.DateTime).Value = dateEnd;
// Execute the command here
}
I'd do the equivalent for Oracle as well - unless your task really inherently involves converting between text and the "native" data type, don't do it.
Or use a LINQ provider of course, at which point you'll have a more readable query to start with :)
Just pass the .Net DateTime value as a parameter to the Sql Command. The database driver will handle the conversion to an Sql Server date time automatically.
I am unable to read value from stored procedure when executing it.
Here is my stored procedure
ALTER Procedure [dbo].[SaveInvitation]
(
#InvitationID INT OUTPUT,
#UserID INT,
#Email NCHAR(100),
#InvitationGUID UNIQUEIDENTIFIER OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
IF #UserID = -1
BEGIN
SET #UserID = NULL
END
IF NOT EXISTS (SELECT 1 FROM Invitations WHERE LTRIM(RTRIM(Email)) = LTRIM(RTRIM(#Email)))
BEGIN
INSERT INTO Invitations (UserID,
Email,
CreateDate)
VALUES (#UserID,
#Email,
GETDATE())
-- GET NEWLY INSERTED INVITATIONS ID
SET #InvitationID = IDENT_CURRENT('Invitations')
-- GET GUID FROM INVITATION ID
SELECT #InvitationGUID = InvitationGUID
FROM Invitations
WHERE InvitationID = #InvitationID
END
ELSE
BEGIN
RAISERROR('ALREADY_INVITATED', 16, 127)
END
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK
END
EXEC ThrowError
END CATCH
END
I am executing this procedure from this function
My DataBaseModel.Designer.cs:
public ObjectResult<SaveInvitation_Result> SaveInvitation(ObjectParameter invitationID, Nullable<global::System.Int32> userID, global::System.String email, ObjectParameter invitationGUID)
{
ObjectParameter userIDParameter;
if (userID.HasValue)
{
userIDParameter = new ObjectParameter("UserID", userID);
}
else
{
userIDParameter = new ObjectParameter("UserID", typeof(global::System.Int32));
}
ObjectParameter emailParameter;
if (email != null)
{
emailParameter = new ObjectParameter("Email", email);
}
else
{
emailParameter = new ObjectParameter("Email", typeof(global::System.String));
}
return base.ExecuteFunction<SaveInvitation_Result>("SaveInvitation", invitationID, userIDParameter, emailParameter, invitationGUID);
}
It throws an exception
The data reader is incompatible with the specified 'TestModel.SaveInvitation_Result'. A member of the type, 'InvitationGUID', does not have a corresponding column in the data reader with the same name.
I have created a complex type i.e. SaveUserRegistration_Result and imported one function SaveInvitation of return type SaveUserRegistration_Result.
How can I solve above exception? Is there any change in stored procedure?
Screen shot
It's giving you this error because you're not actually SELECTing back a result. If you added a line to the end of your procedure for example:
SELECT #InvitationGUID AS InvitationGUID
It should work just fine for you.