I am working with ASP.NET MVC 4 using C# and SQL Server
I am selecting a row of data from the following table
CREATE TABLE [dbo].[Mem_Basic] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Mem_NA] VARCHAR (100) NOT NULL,
[Mem_Occ] VARCHAR (200) NOT NULL,
[Mem_Role] VARCHAR (200) NOT NULL,
[Mem_Email] VARCHAR (50) NULL,
[Mem_MPh] VARCHAR (15) NULL,
[Mem_DOB] DATE NULL,
[Mem_BGr] NCHAR (10) NULL,
[Mem_WAnn] DATE NULL,
[Mem_Spouse] VARCHAR (75) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
using the following code
public MemberBasicData GetMemberProfile(int id)
{
MemberBasicData mb = new MemberBasicData();
using (SqlConnection con = new SqlConnection(Config.ConnectionString))
{
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Mem_Basic WHERE Id="+id+"", con))
{
try
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if(reader.Read()==true)
{
mb.Id = (int)reader["Id"];
mb.Mem_NA = (string)reader["Mem_NA"];
mb.Mem_Occ = (string)reader["Mem_Occ"];
mb.Mem_Role = (string)reader["Mem_Role"];
mb.Mem_Email = (string)reader["Mem_Email"];
mb.Mem_MPh = (string)reader["Mem_MPh"];
mb.Mem_DOB = (Convert.ToDateTime(reader["Mem_DOB"]));
mb.Mem_BGr = (string)reader["Mem_BGr"];
mb.Mem_WAnn = (Convert.ToDateTime(reader["Mem_WAnn"]));
mb.Mem_Spouse = (string)reader["Mem_Spouse"];
}
}
catch (Exception e) { throw e; }
finally { if (con.State == System.Data.ConnectionState.Open) con.Close(); }
}
}
return mb;
}
This shows the error
Unable to cast object of type 'System.DBNull' to type 'System.String'.
(Mem_Email, MPh.. etc sometimes contain a NULL value.. if the value is null I want return null). Anybody please help me.
Just make some short if, you should do the same for all the other variables:
mb.Mem_Email = reader["Mem_Email"] == System.DBNull.Value ? null : (string) reader["Mem_Email"];
You could save yourself a serious amount of pain here with a tool like dapper (http://www.nuget.org/packages/Dapper):
public MemberBasicData GetMemberProfile(int id)
{
using (var con = new SqlConnection(Config.ConnectionString))
{
return con.Query<MemberBasicData>(
"SELECT * FROM Mem_Basic WHERE Id=#id",
new { id } // full parameterization, done the easy way
).FirstOrDefault();
}
}
things this does:
does correct parameterization (for both performance and safety), but without any inconvenience
does all the materialization, handling nulls (both in parameters and columns) for you
is insanely optimized (basically, it is measurably the same speed as writing all that code yourself, except fewer things to get wrong)
Alternatively to King King's answer you can write code like this:
mb.Mem_Email = reader["Mem_Email"] as string;
For value types, if the column allows nulls, it's a good practice to map them to nullable value types in C# so that this code reader["Mem_DOB"] as DateTime? works
Change for all columns, that might be NULL from this
mb.Mem_NA = (string)reader["Mem_NA"];
to that
mb.Mem_NA = reader["Mem_NA"].ToString();
Treat the nullable fields:
mb.Mem_Email = System.DBNull.Value.Equals(reader["Mem_Email"])?"":
(string)reader["Mem_Email"];
Do the same for:
mb.Mem_MPh, mb.Mem_BGr and mb.Mem_Spouse.
I don't mean to sound like a SQL bigot (which of course means I DO mean to sound like a SQL bigot), but if you followed SQL best practices and used a column list instead of SELECT * you could resolve this problem by using COALESCE on the nullable columns thus:
SELECT
[Id],
[Mem_NA],
[Mem_Occ],
[Mem_Role],
COALESCE( [Mem_Email], '' ) AS [Mem_Email],
COALESCE( [Mem_MPh], '' ) AS [Mem_MPh],
COALESCE( [Mem_DOB], CAST( '1753-1-1' AS DATE ) ) AS [Mem_DOB],
COALESCE( [Mem_BGr, '' ) AS [Mem_BGr],
COALESCE( [Mem_WAnn], CAST( '1753-1-1' AS DATE ) ) AS [Mem_WAnn],
COALESCE( [Mem_Spouse], '' ) AS [Mem_Spouse]
FROM
[dbo].[Mem_Basic];
Your c# code can now dependably process the result set without having to account for outliers (the exception being the dates; you should probably check for whatever default you use in the COALESCE for those (I used the minimum allowable value for a SQL Date variable in the above example), and handle them appropriately.
Additionally, you can get rid of the finally block in your c# code. You wrapped the connection in a "using" block; it will automatically close the connection when you go out of scope (that is the purpose of the "using" block).
Related
I m using user define table parameter for bulk insert, i create the user define table and column name is ModifiedDate in datatype for datetime
when i pass the value into sql it will insert fine but it missed milliseconds value then how can i install this
My user define table
CREATE TYPE [dbo].[Test] AS TABLE(
[ModifiedDate] [datetime] NOT NULL,
)
My Sp
ALTER PROCEDURE [dbo].[CP_UpdateData]
-- Add the parameters for the stored procedure here
#Test Test Readonly,
INSERT into Test(ModifiedDate)
Values(ModifiedDate);
but here my datetime value is missing millisecond, could you please help any suggestion for resolve this issues
in by c# code
using (var cmd = new SqlCommand())
{
cmd.CommandText = "CP_UpdateData";
cmd.Connection = con;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("Test", SqlDbType.Structured).Value = ConvertToDataTable(list);
con.Open();
var dataReader = await cmd.ExecuteReaderAsync();
}
public DataTable ConvertToDataTableCampaingList(List<Test> list)
{
var dataTable = new DataTable();
if (list != null && list.Count > 0)
{
dataTable.Columns.Add("ModifiedDate", Type.GetType("System.DateTime"));
foreach (var data in list)
{
var dataRow = dataTable.NewRow();
dataRow["ModifiedDate"] = data.ModifiedDate;
dataTable.Rows.Add(dataRow);
}
}
return dataTable;
}
The answer being in the CHAT ROOM discussion, I will post it here:
The problem is implicit conversions and how each compiler treats the data. DATETIME by default has no defined FORMAT, so SQL implicitly converts the data.
So the issue is when your storing it into the table as the Default
formatting is the problem CREATE TABLE #Example2 (TIMES DATETIME NOT
NULL) INSERT INTO #Example2 (TIMES) VALUES (CONVERT(DATETIME,
GETDATE(), 9)) , (CAST(GETDATE() AS VARCHAR(20) ) ) Notice how the
default in a simple string actually drops milliseconds since its
format is wrong
Notice the solution was explicitly defining the format:
Manikandan wrote:
When i convert the var date =
(DateTime.Parse(datetime.ToString("yyyy-MM-dd HH:mm:ss.fff"))); it
return correct milliseconds
Converting the DATETIME to string makes it portable and in a data type that will not TRUNCATE the data. However, use a proper CONVERT(data type, expression, style) if you ensure accuracy.
Manikandan wrote:
DECLARE #TEMP_Result TABLE
(
ModifiedDate DATETIME
)
DECLARE #TEMp TABLE
(
ModifiedDate varchar(50)
)
declare #timestring varchar(50)
set #timestring = '2016-06-28 12:53:20.850'
Insert into #TEMp(ModifiedDate)
values(#timestring)
Insert into #TEMP_Result(ModifiedDate)
select Convert(datetime, ModifiedDate) from #TEMp
select * from #TEMP_Result
MORAL: BEWARE OF IMPLICIT CONVERSIONS
Implicit conversion are guesses and determined by the compiler. They are not dependable as this case shows.
CAST is not an explicit conversion, and may return the wrong format. Use CONVERT in SQL to avoid implicit conversions.
Storing DATETIME in a string makes it portable, avoids TRUNCATION of data, and is easily converted to the correct format in SQL.
I am trying to store values in database.
How can I assign value NULL in the database, if it a an int.
Query:
#id varchar(10)
UPDATE [Table] SET
[id] = #id -- id is an int in sql
C# code:
I pass the value:
//dATABASE CONNECTION
db.StoredProcedure = "UpdateProjectValues";
db.Paramater("ID",TB_ID.TEXT); //tb_id.text=""
If it has value then that int will be stored but if I do not have the value, then 0 is getting stored, but i want NULL to be stored.
One solution strictly inside the database is:
UPDATE [Table]
SET [id] = (case when #id <> '' then cast(#id as int) end);
This makes the conversion explicit. However, it is probably better to set up the C# code to pass in an integer value that can take on a NULL value.
I thought I'd provide a few alternatives:
1). Handle the value in your stored procedure. Gordon's CASE statement works but since you are using SQL Server you also have the NULLIF() function which you can use like this:
UPDATE TABLE
SET [id] = NULLIF(#ID, '');
2.) Handle this in the C# code using a ternary operator like:
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "UpdateProjectValues";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ID", string.IsNullOrWhiteSpace(TB_ID.TEXT) ? DBNull.Value : (object)TB_ID.TEXT);
There are other ways also, but these are simple examples.
During the development of an windows mobile application we used the following code:
const string sqlString2 =
#"INSERT INTO WorkOrderComment
(WOInstructionID,WorkOrderCommentID,ClientChange,Void,UserID,VoidTimestamp)
(SELECT WOInstructionID, WOInstructionID, 1, #VoidBit,#VoidUser ,#VoidTimeStamp
FROM WorkOrderInstruction
WHERE InstructionGroupQuestion = 0 AND InstructionGroupNumber = #insGroupNo AND WorkOrderID=#WoID)";
var sqlcom2 = new SqlCeCommand();
sqlcom2.CommandType = CommandType.Text;
sqlcom2.CommandText = sqlString2;
sqlcom2.Parameters.Add("#VoidBit", action);
sqlcom2.Parameters.Add("#VoidUser", DBNull.Value);
sqlcom2.Parameters.Add("#VoidTimeStamp", DBNull.Value);
sqlcom2.Parameters.Add("#WoID", workOrderID);
sqlcom2.Parameters.Add("#insGroupNo", instructionGroupNumber);
sqlcom2.ExecuteNonQuery();
When running this with I receive the following error on ExecuteNonQuery:
The conversion is not supported. [ Type to convert from (if known) = int, Type to convert to (if known) = uniqueidentifier ]
Even if I add sqlcom2.Parameters["#VoidUser"].DbType = DbType.Guid; I got the same error. After a while I found that this could be helped with using CONVERT.
I changed #VoidUser to CONVERT(uniqueidentifier,#VoidUser).
This resulted in the following error: A parameter is not allowed in this location. Ensure that the '#' sign is in a valid location or that parameters are valid at all in this SQL statement.
After a couple of hours of reading documentation and other posts on why I could not use variables at these places I just tried to put in CONVERT everywhere; changing #VoidBit to CONVERT(bit,#VoidBit) and #VoidTimeStamp to CONVERT(datetime,#VoidTimeStamp).
And for some reason it works.
The table is created with this command:
CREATE TABLE WorkOrderComment ( WorkOrderCommentID uniqueidentifier NOT NULL CONSTRAINT WorkOrderCommentPK Primary Key,
WOInstructionID uniqueidentifier NOT NULL,
WorkOrderID_Update uniqueidentifier NULL,
TextData NTEXT NULL,
Value float NULL,
Category nvarchar(50) NULL,
BarcodeScanned bit NULL,
Timestamp DateTime NULL,
Time float NULL,
UserID uniqueidentifier NULL,
Void bit NULL,
VoidTimeStamp DateTime NULL,
FaultComplaintID uniqueidentifier NULL,
ClientChange bit NOT NULL)
Why am I not allowed to use "naked" parameters?
Why did it throw an error on the conversion in the first place, and not the usage of a parameter?
The Parameters.Add() method takes 2 parameters: Parameter name and data type. It seems you are confusing with Parameters.AddWithValue()
Also SQLCE seems to be more difficult with type conversion than SQL Server. So the best is you specifically mention the type of the parameters. (it only really matters for uniqueidentifier though)
I would slightly rewrite the code like this as well:
using (var sqlConn = new SqlCeConnection(connStr))
using (var sqlCmd = new SqlCeCommand(sqlString2, sqlConn))
{
sqlConn.Open();
sqlCmd.Parameters.Add("#VoidBit", SqlDbType.Bit).Value = action;
sqlCmd.Parameters.Add("#VoidUser", SqlDbType.UniqueIdentifier).Value = DBNull.Value;
sqlCmd.Parameters.Add("#VoidTimeStamp", SqlDbType.DateTime).Value = DBNull.Value;
sqlCmd.Parameters.Add("#WoID", SqlDbType.UniqueIdentifier).Value = workOrderID;
sqlCmd.Parameters.Add("#insGroupNo", SqlDbType.Int).Value = instructionGroupNumber;
sqlCmd.ExecuteNonQuery();
}
See https://gist.github.com/1932722 for full repro script
I wrote an little app in C# to keep track of customers and jobs but I get an error
"There was an error parsing the query.[Token line number = 1, Token
line offset = 14,Token in error = ']
I'm using a SQL Server CE .sdf database
public static void CreateEmployeeTable(string name)
{
conn.Open();
using (SqlCeCommand cmd = conn.CreateCommand())
{
cmd.CommandText = #"CREATE TABLE `#Name` (
`Id` INT(10) NOT NULL AUTO_INCREMENT,
`Job` VARCHAR(50) NULL,
`Pay` VARCHAR(50) NULL,
`TotalJobs` VARCHAR(50) NULL,
`TotalPay` VARCHAR(50) NULL,
PRIMARY KEY (`Id`)";
cmd.Parameters.AddWithValue("#Name", name);
cmd.ExecuteNonQuery();
}
}
I researched the error a lot and seems this error is pretty vague
You cannot parameterise a CREATE table statement that way.
Simply construct the CommandText string in code using string concatenation (being careful of possible SQL Injection vulnerabilities) and execute it.
You cannot parameterize the table name in your example. I altered your create table SQL to conform to supported data types for SQL CE. Please note that the NVARCHAR statements are set to a length of 100 to support up to the 50 characters you were specifying (VARCHAR is not supported).
I tested this code sample, but there are a couple more things to think about:
Your method is currently public. I don't know the context in which you are calling this, but consider making it private or internal.
Consider querying first to determine whether the table already exists to avoid an exception.
If at all possible I would create the connection in a using block directly above your command creation. That way you will know the connection gets closed and disposed. I'll leave that to your discretion.
public static void CreateEmployeeTable(string name)
{
const string createTableFormat = #"CREATE TABLE [{0}] (
Id INT IDENTITY NOT NULL PRIMARY KEY,
Job NVARCHAR(100) NULL,
Pay NVARCHAR(100) NULL,
TotalJobs NVARCHAR(100) NULL,
TotalPay NVARCHAR(100) NULL)";
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException("name");
}
// Just replace with your connection string.
using (SqlCeConnection cn = new SqlCeConnection(Settings.Default.LocalDbConnectionString))
using (SqlCeCommand cmd = cn.CreateCommand())
{
cmd.CommandText = string.Format(createTableFormat, name);
cn.Open();
cmd.ExecuteNonQuery()
}
}
I'm using jquery ajax to send values to my .net web service which in turn sends those values to a stored procedure. The problem i'm having is that no matter what values I send to the web service, they get inserted into my DB as one char. e.g. "test" becomes "t".
None of my receiving tables columns have a max value set of 1. Using firebug for Firefox I have confirmed that my ajax method is indeed posting the intended values to the web service.
Web Service:
[WebMethod]
public void InsertAllVehicles(string Make, string Model, string SubModel, decimal Liter, string Cylinder,string Fuel, string FuelDel, string Asp, string EngDesg)
{
using (SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["localConnectionString"].ConnectionString))
{
using (SqlCommand comm = new SqlCommand())
{
conn.Open();
comm.Connection = conn;
comm.CommandType = CommandType.StoredProcedure;
comm.CommandText = "Vehicle_InsertAll";
if (Make.Length >=1)
comm.Parameters.AddWithValue("#Make", Make);
if (Model.Length >=1)
comm.Parameters.AddWithValue("#Model", Model);
if (SubModel.Length >=1)
comm.Parameters.AddWithValue("#SubModel", SubModel);
if (Liter != 91)
comm.Parameters.AddWithValue("#Liter", Liter);
if (Cylinder.Length >= 1)
comm.Parameters.AddWithValue("#Cylinder", Cylinder);
if (Fuel.Length >= 1)
comm.Parameters.AddWithValue("#Fuel", Fuel);
if (FuelDel.Length >= 1)
comm.Parameters.AddWithValue("#FuelDel", FuelDel);
if (Asp.Length >= 1)
comm.Parameters.AddWithValue("#Asp", Asp);
if (EngDesg.Length != 0)
comm.Parameters.AddWithValue("#EngDesg", EngDesg);
comm.ExecuteNonQuery();
conn.Close();
}
}
}
Stored Procedure
ALTER PROCEDURE dbo.Vehicle_InsertAll
#Make varchar = null,
#Model varchar = null,
#SubModel varchar = null,
#Liter decimal = null,
#Cylinder varchar = null,
#Fuel varchar = null,
#FuelDel varchar = null,
#Asp varchar = null,
#EngDesg varchar = null
AS
IF #Make IS NOT NULL
BEGIN
INSERT INTO VehicleMakes (VehicleMake) VALUES(#Make)
END
IF #Model IS NOT NULL
BEGIN
INSERT INTO VehicleModels (Name) VALUES(#Model)
END
IF #SubModel IS NOT NULL
BEGIN
INSERT INTO VehicleSubModels (Name) VALUES(#SubModel)
END
IF #Liter IS NOT NULL
BEGIN
INSERT INTO VehicleLiters (Liters) VALUES(#Liter)
END
IF #Cylinder IS NOT NULL
BEGIN
INSERT INTO VehicleCylinders (Cylinders) VALUES(#Cylinder)
END
IF #Fuel IS NOT NULL
BEGIN
INSERT INTO VehicleFuel (FuelType) VALUES (#Fuel)
END
IF #FuelDel IS NOT NULL
BEGIN
INSERT INTO VehicleFuelDelivery (Delivery) VALUES (#FuelDel)
END
IF #Asp IS NOT NULL
BEGIN
INSERT INTO VehicleAspiration (Asp) VALUES (#Asp)
END
IF #EngDesg IS NOT NULL
BEGIN
INSERT INTO VehicleEngDesg (Designation) VALUES (#EngDesg)
END
RETURN
The parameters of your stored procedure, particularly those with type varchar should be declared with lengths that match their columns defined in the table. For example: #Make varchar(50)=null.
Because VARCHAR is one character. Set it to VARCHAR(MAX) till you know what size you really want, replacing MAX with the size you need.
specify the varchar length of each field in your stored proc, eg #Make varchar(50) = null