I have a windows form and I'm inserting values in the button click event like this
Candidate CanObj = new Candidate(txtName.Text);
if (new CandidateOP().saveCandidate(CanObj))
{
MessageBox.Show("NEW candidate details added");
}
this is my business layer method.
public Boolean saveCandidate(Candidate CanObj)
{
string query6 = "EXEC insertToCand01'" + CanObj.NIC + "'";
return (new DataAccessLayer().executeNonQueries(query6));
}
This is my data access layer method
public Boolean executeNonQueries(string query02)
{
Boolean flag = false;
SqlConnection con = null;
SqlCommand com = null;
try
{
con = new SqlConnection(DBConnect.makeConnection());
con.Open();
com = new SqlCommand(query02, con);
com.ExecuteNonQuery();
flag = true;
}
catch (Exception ex)
{
flag = false;
throw ex;
}
finally
{
com.Dispose();
con.Close();
}
return flag;
}
This is the query inside my stored procedure to insert.
In my table the ID is set to auto increment.
INSERT INTO Candidate (User_Name) VALUES (#Uname);
Now I want to display the inserted ID to be displayed when it's inserted.
So I changed the query like this.
INSERT INTO Candidate (User_Name) OUTPUT INSERTED.User_ID VALUES (#Uname);
I want to change my data access layer and business layer to get the value back
How to change my data access layer to achieve this?
Thanks in advance.
Just a quick but important note: you should really use parameterized queries to avoid SQL injection problems, and also using a proper ORM system.
About your concrete question: call your procedure with ExecuteScalar, instead of ExecuteNonQuery, and return the generated id from your stored procedure.
You don't actually need an SP, you can just do a select scope_identity() for example. Or you could use an output parameter in your SP. But just returning a scalar is the simplest way.
Something like this:
Candidate CanObj = new Candidate(txtName.Text);
int id = new CandidateOP().saveCandidate(CanObj);
/* You have **id** here, and you can use it. */
if (id >= 0)
{
MessageBox.Show("NEW candidate details added");
}
Business layer:
public Boolean saveCandidate(Candidate CanObj)
{
string query6 = "EXEC insertToCand01'" + CanObj.NIC + "'";
return new DataAccessLayer().executeNonQueries(query6);
}
and your access layer:
public int executeNonQueries(string query02)
{
long id = -1;
SqlConnection con = null;
SqlCommand com = null;
try
{
con = new SqlConnection(DBConnect.makeConnection());
con.Open();
com = new SqlCommand(query02, con);
SqlParameter returnParameter = com.Parameters.Add("RetVal", SqlDbType.Int);
returnParameter.Direction = ParameterDirection.ReturnValue;
com.ExecuteNonQuery();
id = (int) returnParameter.Value;
}
catch (Exception ex)
{
id = -1;
throw ex;
}
finally
{
com.Dispose();
con.Close();
}
return id;
}
Related
How to loop through a list of objects in order to pass said object to a method inserting rows in SQL db via stored procedure?
With help in this question I got to this point:
namespace NA.Controllers
{
public class NC : ApiController
{
[Route("AddNote")]
[HttpPost]
public HttpResponseMessage PostNote(List<Note> items)
{
//NoteJson deserializednote = JsonConvert.DeserializeObject<NoteJson>(item);
//Note notesdata = new Note(item);
NotesAccept.Models.INoteRepository Repository = new NotesAccept.Models.NoteDataRepository();
foreach (Note item in items)
{
item = Repository.Add(item);
}
var response = Request.CreateResponse<List<Note>>(HttpStatusCode.OK, items);
return response;
}
}
}
but now I'm stuck as item= is now an iteration variable, but I need to pass it to an method:
namespace NA.Models
{
class NoteDataRepository : INoteRepository
{
public void Add(Note item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
else
{
String strConnString = ConfigurationManager.ConnectionStrings["conString"].ConnectionString;
SqlConnection con = new SqlConnection(strConnString);
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "BL_IntegrationInsertNote";
cmd.Parameters.Add("#Client", SqlDbType.VarChar).Value = item.Client.Trim();
cmd.Parameters.Add("#Case", SqlDbType.VarChar).Value = item.Case;
cmd.Parameters.Add("#Text", SqlDbType.VarChar).Value = item.Text.Trim();
cmd.Parameters.Add("#When", SqlDbType.DateTime).Value = item.Date;
cmd.Parameters.Add("#Ext", SqlDbType.Bit).Value = item.Type;
cmd.Parameters.Add("#return", SqlDbType.Int).Direction = ParameterDirection.Output;
cmd.Connection = con;
try
{
con.Open();
cmd.ExecuteNonQuery();
string id = cmd.Parameters["#return"].Value.ToString();
string lblMessage = null;
lblMessage = "Record inserted successfully. ID = " + id;
}
catch (Exception ex)
{
throw ex;
}
finally
{
con.Close();
con.Dispose();
}
}
//return item;
}
IEnumerable<Note> INoteRepository.GetAll()
{
throw new NotImplementedException("getitems");
}
}
}
I'm still green-as-a-grass-newbie to C# so I've no idea how to accomplish that, especially since whole solution is still "copy&Paste" from all over the web, and whole web curiously concentrates on looping through simple types. How to do that with complex type?
As noted in other question, this is a matter of professional life and death (I'm a db dev, not a VS guru, especially not after two days and two nights).
You are still forgetting to assign that ID from DB to the item.
You also still have
return item;
in a method that does not return anything (public void Add(Note item)).
So just delete that return line.
And replace
item = Repository.Add(item);
with just
Repository.Add(item);
You can pass it as xml and iterate in sql stored procedure and do bulk insert, or you can use table datatype if sql and .net version you are using supports it.
Try this in the foreach loop:
var tempItem = item;
tempItem = Repository.Add(tempItem);
I'm trying to fill a combo box on from load from a database, I'm getting the error "Invalid object name 'POOL'"
Form load event to populate dropdown on form load
private void FRMAddTeam_Load(object sender, EventArgs e)
{
if (CMBBXPool.Items.Count > 0)
CMBBXPool.Items.Clear();
Database.CLSDB DatabaseClass = new Database.CLSDB();
DatabaseClass.FillDropDownList();
}
This is the code in my database connection class
public void FillDropDownList()
{
string PoolName = "";
Team.FRMAddTeam TeamAdd = new Team.FRMAddTeam();
SqlConnection conn = GetConnection();
string selStmt = "SELECT [Name] FROM dbo.TBL_pool";
SqlCommand selCmd = new SqlCommand(selStmt, conn);
try
{
conn.Open();
SqlDataReader reader = selCmd.ExecuteReader();
while (reader.Read())
{
PoolName = reader["Name"].ToString();
TeamAdd.addPoolItem(PoolName);
}
}
catch (SqlException ex) { throw ex; }
finally { conn.Close(); }
return;
}
Code to add the pool name
public void addPoolItem(string PoolName)
{
CMBBXPool.Items.Add(PoolName);
}
Any help is much appreciated
Your code needs to be:
public void FillDropDownList(Team.FRMAddTeam TeamAdd)
{
string PoolName = "";
SqlConnection conn = GetConnection();
string selStmt = "SELECT [Name] FROM dbo.TBL_pool";
SqlCommand selCmd = new SqlCommand(selStmt, conn);
try
{
conn.Open();
SqlDataReader reader = selCmd.ExecuteReader();
while (reader.Read())
{
PoolName = reader["Name"].ToString();
TeamAdd.addPoolItem(PoolName);
}
}
catch (SqlException ex) { throw ex; }
finally { conn.Close(); }
return;
}
You call it from your form like this:
DatabaseClass.FillDropDownList(this);
This will work, however it is strongly advised to change the implementation of your database class and remove tight coupling with GUI.
It is WRONG to fill your GUI from database class - instead of that, you should return data from your database class and bind your data to GUI in the GUI class.
http://en.wikipedia.org/wiki/Loose_coupling
http://en.wikipedia.org/wiki/Object_orgy
http://en.wikipedia.org/wiki/Separation_of_concerns
Sounds like you dont have table Pool.Are you sure its there?
Log into your SQL management studio and use query analyzer to run the same command.
There could be many reasons for that If your schema is different or do you have permission to access that table ? or are you checking the right database ?
UPDATE:
You should try using SELECT [Name] FROM dbo.TBL_Pool
I have a store procedure which is taken 2 parameter 1st projectId and 2nd Userid
But i have to mapped the list to that particular project using single connection but i could not figure out how to insert whole list.
internal bool MapEmployeesToProject(int projectId, List<Users> lstUserToMap)
{
int value = 0;
bool check = true;
SqlConnection conn = new SqlConnection(VSTMConfigurations.ConnectionString);
using (conn)
{
SqlParameter[] param = new SqlParameter[2];
param[0] = new SqlParameter("#ProjectId",projectId );
param[1] = new SqlParameter("#ProjectDetails", );
// if there is a single user than i could pass userid here
// i am phasing a problem how to pass whole listOfUsers
using (SqlCommand cmd1 = CreateCommand(conn, param))
{
cmd1.CommandText = "sp_MapProject";
try
{
conn.Open();
SqlTransaction transaction = conn.BeginTransaction();
cmd1.Transaction = transaction;
try
{
value = Convert.ToInt32(cmd1.ExecuteNonQuery());
if (value > 0)
{
transaction.Commit();
check = true;
}
else
{
transaction.Rollback();
check = false;
}
}
catch (Exception ex1)
{
transaction.Rollback();
check = false;
}
finally
{
transaction.Dispose();
}
}
catch (Exception ex)
{
check = false;
}
}
}
return check;
}
the store procedure is
sp_MapProject
(#ProjectId int,#UserId int)
as
begin
Insert into ProjectUsers values (#ProjectId,#UserId)
end
help please!!
You can define a user define table type in your database with projectId and Userid columns. Then you can use DataTable to pass data to stored procedure.
e.g. http://www.codeproject.com/Articles/412802/Sending-a-DataTable-to-a-Stored-Procedure
Thanks!
In my WindowsCE / Compact Framework (.NET1.1) project, I need to create a new table in code. I thought I could do it this way:
if (! TableExists("table42"))
{
CreateTable42();
}
public static bool TableExists(string tableName)
{
try
{
using (SqlCeConnection sqlConn = new SqlCeConnection(#"Data Source=\my documents\Platypus.SDF"))
{
sqlConn.Open();
string qryStr = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ?";
SqlCeCommand cmd = new SqlCeCommand(qryStr, sqlConn);
cmd.Parameters[0].Value = tableName;
cmd.CommandType = CommandType.Text;
int retCount = (int)cmd.ExecuteScalar();
return retCount > 0;
}
}
catch (Exception ex)
{
MessageBox.Show("TableExists ex.Message == " + ex.Message);
MessageBox.Show("TableExists ex.ToString() == " + ex.ToString());
MessageBox.Show("TableExists ex.GetBaseException() == " + ex.GetBaseException());
return false;
}
}
...but the call to TableExists() fails; and shows me:
TableExists ex.Message ==
TableExists ex.ToString() == System.Data.SqlServerCe.SqlCeException at System.Data.SqlServerCe.SqlConnection.ProcessResults(Int32 hr) at ...at Open(boolean silent) ...
TableExists ex.GetBaseException() == [same as ex.ToString() above]
"Int32 hr" ... ??? What the Hec Ramsey is that?
As documented previously in these environs, I can't step through this projct, so I rely on those calls to MessageBox.Show().
The rest of the related code, if it may be of interest, is:
public static void CreateTable42()
{
try
{
using (SqlCeConnection con = new SqlCeConnection(#"Data Source=\my documents\Platypus.SDF"))
{
con.Open();
using (SqlCeCommand com = new SqlCeCommand(
"create table table42 (setting_id INT IDENTITY NOT NULL PRIMARY KEY, setting_name varchar(40) not null, setting_value(63) varchar not null)", con))
{
com.ExecuteNonQuery();
WriteSettingsVal("table42settingname","table42settingval");
}
}
}
catch (Exception ex)
{
MessageBox.Show("CreateTable42 " + ex.Message);
}
}
public static void WriteSettingsVal(string settingName, string settingVal)
{
using (SqlCeConnection sqlConn = new SqlCeConnection(#"Data Source=\my documents\Platypus.SDF"))
{
sqlConn.Open();
string dmlStr = "insert into tabld42 (setting_name, setting_value) values(?, ?)";
SqlCeCommand cmd = new SqlCeCommand(dmlStr, sqlConn);
cmd.CommandType = CommandType.Text;
cmd.Parameters[0].Value = settingName;
cmd.Parameters[1].Value = settingVal;
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show("WriteSettingsVal " + ex.Message);
}
}
}
UPDATE
Answer to Brad Rem's comment:
I don't think it's necessary to encase the param in quotes, as other working code is like:
cmd.Parameters.Add("#account_id", Dept.AccountID);
-and:
cmd.Parameters[0].Value = Dept.AccountID;
(it does it one way the first time when in a loop, and the other way thereafter (don't ask me why).
Anyway, just for grins, I did change the TableExists() parameter code from this:
cmd.Parameters[0].Value = tableName;
...to this:
cmd.Parameters.Add("#TABLE_NAME", tableName);
...but I still get the exact same result.
UPDATE 2
Here (http://msdn.microsoft.com/en-us/library/aa237891(v=SQL.80).aspx) I found this: "Caution You must specify the SQL Server CE provider string when you open a SQL Server CE database."
They give this example:
cn.ConnectionString = "Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0; data source=\Northwind.sdf"
I'm not doing that; my conn str is:
using (SqlCeConnection sqlConn = new SqlCeConnection(#"Data Source=\my documents\CCRDB.SDF"))
Could that be my problem?
UPDATE 3
I took this gent's advice (http://www.codeproject.com/Answers/629613/Why-is-my-SQLServer-CE-code-failing?cmt=487657#answer1) and added a catch for SqlCeExcpetions so that it is now:
public static bool TableExists(string tableName)
{
try
{
using (SqlCeConnection sqlConn = new SqlCeConnection(#"Data Source=\my documents\CCRDB.SDF"))
{
sqlConn.Open();
string qryStr = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #TABLE_NAME";
SqlCeCommand cmd = new SqlCeCommand(qryStr, sqlConn);
cmd.Parameters.Add("#TABLE_NAME", tableName);
cmd.CommandType = CommandType.Text;
int retCount = (int)cmd.ExecuteScalar();
return retCount > 0;
}
}
catch (SqlCeException sqlceex)
{
MessageBox.Show("TableExists sqlceex.Message == " + sqlceex.Message);
MessageBox.Show("TableExists sqlceex.ToString() == " + sqlceex.ToString());
return false;
. . .
The SqlCeException message is: "There is a file sharing violation. A different process might be using the file [,,,,,]" then "...processresults ... open ... getinstance ..."
UPDATE 4
Trying to use ctacke's sample code, but: Is Transaction absolutely necessary? I had to change the code to the following for my scenario/milieu, and don't know what Transaction should be or how to build it:
public static bool TableExists(string tableName)
{
string sql = string.Format("SELECT COUNT(*) FROM information_schema.tables WHERE table_name = '{0}'", tableName);
try
{
using (SqlCeConnection sqlConn = new SqlCeConnection(#"Data Source=\my documents\HHSDB.SDF"))
{
SqlCeCommand command = new SqlCeCommand(sql, sqlConn);
//command.Transaction = CurrentTransaction as SqlCeTransaction;
command.Connection = sqlConn;
command.CommandText = sql;
int count = Convert.ToInt32(command.ExecuteScalar());
return (count > 0);
}
}
catch (SqlCeException sqlceex)
{
MessageBox.Show("TableExists sqlceex.Message == " + sqlceex.Message);
return false;
}
}
UPDATE 5
With this code, the err msg I get is, "An err msg is available for this exception but cannot be displayed because these messages are optional and are not currently insallted on this device. Please install ... NETCFv35.Messages.EN.cab"
UPDATE 6
All too typically, this legacy, ancient-technology project is giving me headaches. It seems that only one connection is allowed to be open at a time, and the app opens one from the outset; so, I have to use that connection. However, it is a DBConnection, not a SqlCeConnection, so I can't use this code:
using (SqlCeCommand com = new SqlCeCommand(
"create table hhs_settings (setting_id int identity (1,1) Primary key, setting_name varchar(40) not null, setting_value(63) varchar not null)", frmCentral.dbconn))
{
com.ExecuteNonQuery();
WriteSettingsVal("beltprinter", "ZebraQL220");
}
...because the already-open connection type passed as an arg to the SqlCeCommand constructor is DBCommand, not the expected/required SqlCeConneection.
The tentacles of this code are far too wide and entrenched to rip out by the roots and refactor to make it more sensible: a single tentative step in the foothills causes a raging avalanche on Everest.
For fun I'd try two things. First, replace the '?' parameter with a named parameter like '#tablename' and see if that changes things. Yes, I know '?' should work, but it's a confusing, ugly precedent and maybe since it's a system table it's wonky. Yes, it's a stretch, but worth a try just to know.
The second thing I'd do is something like this method from the SQLCE implementation of the OpenNETCF ORM:
public override bool TableExists(string tableName)
{
var connection = GetConnection(true);
try
{
using (var command = GetNewCommandObject())
{
command.Transaction = CurrentTransaction as SqlCeTransaction;
command.Connection = connection;
var sql = string.Format("SELECT COUNT(*) FROM information_schema.tables WHERE table_name = '{0}'", tableName);
command.CommandText = sql;
var count = Convert.ToInt32(command.ExecuteScalar());
return (count > 0);
}
}
finally
{
DoneWithConnection(connection, true);
}
}
Note that I didn't even bother parameterizing, largely because I doubt it will provide any perf benefit (queue the hordes whining about SQL injection). This way definitely works - we've got it deployed and in use in many live solutions.
EDIT
For completeness (though I'm not sure it adds to clarity).
protected virtual IDbConnection GetConnection(bool maintenance)
{
switch (ConnectionBehavior)
{
case ConnectionBehavior.AlwaysNew:
var connection = GetNewConnectionObject();
connection.Open();
return connection;
case ConnectionBehavior.HoldMaintenance:
if (m_connection == null)
{
m_connection = GetNewConnectionObject();
m_connection.Open();
}
if (maintenance) return m_connection;
var connection2 = GetNewConnectionObject();
connection2.Open();
return connection2;
case ConnectionBehavior.Persistent:
if (m_connection == null)
{
m_connection = GetNewConnectionObject();
m_connection.Open();
}
return m_connection;
default:
throw new NotSupportedException();
}
}
protected virtual void DoneWithConnection(IDbConnection connection, bool maintenance)
{
switch (ConnectionBehavior)
{
case ConnectionBehavior.AlwaysNew:
connection.Close();
connection.Dispose();
break;
case ConnectionBehavior.HoldMaintenance:
if (maintenance) return;
connection.Close();
connection.Dispose();
break;
case ConnectionBehavior.Persistent:
return;
default:
throw new NotSupportedException();
}
}
wow... still struggling... I did too when I first got started on a handheld device SQL-CE. My current project is running with C#.Net 3.5 but I think the principles you are running into are the same. Here is what is working for my system in it's close parallels to yours.
First, the connection string to the handheld. It is just
string myConnString = #"Data Source=\MyFolder\MyData.sdf";
no reference to the sql driver
Next, the TableExists
SqlCeCommand oCmd = new SqlCeCommand( "select * from INFORMATION_SCHEME.TABLES "
+ " where TABLE_NAME = #pTableName" );
oCmd.Parameters.Add( new SqlCeParameter( "pTableName", YourTableParameterToFunction ));
The "#pTableName" is to differentiate between the "TABLE_NAME" column and to absolutely prevent any issues about ambiguity. The Parameter does NOT get the extra "#". In SQL, the # indicates to look for a variable... The SqlCeParameter of "pTableName" must match as it is in the SQL Command (but without the leading "#").
Instead of issuing a call to ExecuteScalar, I am actually pulling the data down into a DataTable via
DataTable oTmpTbl = new DataTable();
SqlCeDataAdapter da = new SqlCeDataAdapter( oCmd );
da.Fill( oTmpTbl );
bool tblExists = oTbl.Rows.Count > 0;
This way, I either get records back or I dont... if I do, the number of records should be > 0. Since I'm not doing a "LIKE", it should only return the one in question.
When you get into your insert, updates and deletes, I have always tried to prefix my parameters with something like "#pWhateverColumn" and make sure the SqlCeParameter is by the same name but without the "#". I haven't had any issues and this project has been running for years. Yes it's a .net 3.5 app, but the fundamental basics of connecting and querying SHOULD be the same.
If it IS all within your application, I would try something like creating a single global static "Connection" object. Then, a single static method to handle it. Then, instead of doing a NEW connection during every "using" attempt, change it to something like...
public static class ConnectionHandler
{
static SqlCeConnection myGlobalConnection;
public static SqlCeConnection GetConnection()
{
if( myGlobalConnection == null )
myGlobalConnection = new SqlCeConnection();
return myGlobalConnection;
}
public static bool SqlConnect()
{
GetConnection(); // just to ensure object is created
if( myGlobalConnection.State != System.Data.ConnectionState.Open)
{
try
{
myGlobalConnection.ConnectionString = #"Data Source=\MyFolder\MyDatabase.sdf";
myGlobalConnection.Open();
}
catch( Exception ex)
{
// optionally messagebox, or preserve the connection error to the user
}
}
if( myGlobalConnection.State != System.Data.ConnectionState.Open )
MessageBox.Show( "notify user");
// return if it IS successful at opening the connection (or was already open)
return myGlobalConnection.State == System.Data.ConnectionState.Open;
}
public static void SqlDisconnect()
{
if (myGlobalConnection!= null)
{
if (myGlobalConnection.State == ConnectionState.Open)
myGlobalConnection.Close();
// In case some "other" state, always try to force CLOSE
// such as Connecting, Broken, Fetching, etc...
try
{ myGlobalConnection.Close(); }
catch
{ // notify user if issue}
}
}
}
... in your other class / function...
if( ConnectionHandler.SqlConnect() )
Using( SqlCeConnection conn = ConnectionHandler.GetConnection )
{
// do your stuff
}
... finally, when your app is finished, or any other time you need to...
ConnectionHandler.SqlDisconnect();
This keeps things centralized, and you don't have to worry about open/close, what the connection string is buried all over the place, etc... If you can't connect, you can't run a query, don't try to run the query if it can't even get that far.
I think it may be a permission issue on INFORMATION_SCHEMA system views. Try the following.
GRANT VIEW DEFINITION TO your_user;
See here for more details
Here is my stored procedure on updating records :
ALTER
PROCEDURE [dbo].[sp_UpdatetoShipped]
(
#Date datetime,
#SerialNumber
varchar(50),
#User
varchar(50),
#WorkWeek
varchar(50)
)
AS
BEGIN
UPDATE dbo.FG_FILLIN SET Status='SHIPPED',DateModified=#Date,ModifiedBy=#User,WorkWeek=#WorkWeek where (Status='KITTED')and SerialNumber=#SerialNumber
END
Then this is my DAL:
public int UpdatetoShipped(FillinEntity fin)
{
SqlConnection conn = new SqlConnection(connStr);
conn.Open();
SqlCommand cmd = new SqlCommand("sp_UpdatetoShipped", conn);
cmd.CommandType =CommandType.StoredProcedure;
try
{
cmd.Parameters.Add("#SerialNumber", SqlDbType.VarChar,50).Value = fin.SerialNumber;
cmd.Parameters.Add("#WorkWeek", SqlDbType.VarChar, 50).Value = fin.WorkWeek;
cmd.Parameters.Add("#Date", SqlDbType.DateTime).Value = DateTime.Now.ToString();
cmd.Parameters.AddWithValue("#User", fin.ModifiedBy);
return cmd.ExecuteNonQuery();
}
catch
{
throw;
}
finally
{
cmd.Dispose();
conn.Close();
conn.Dispose();
}
}
My BLL:
public int UpdatetoShipped(FillinEntity fin)
{
DAL pDAL = new DAL();
try
{
return pDAL.UpdatetoShipped(fin);
}
catch
{
throw;
}
finally
{
pDAL = null;
}
}
And MY UI:
string filepath2 = txtPath2.Text;
Stream stream2 = new FileStream(filepath2, FileMode.Open, FileAccess.Read, FileShare.Read);
ExcelMapper<FillinEntity> exceltoshipped = new ExcelMapper<FillinEntity>();
IExcelParser excelParser2 = new ExcelReaderExcelParser(stream2);
IExcelRowMapper<FillinEntity> mapper2 = new ShippedRowMapper();
IEnumerable<FillinEntity> fillin2 = exceltoshipped.ListAll(excelParser2, mapper2);
int intResult = 0;
BAL pBAL = new BAL();
try
{
foreach (FillinEntity fin in fillin2)
{
fin.ModifiedBy = loggedUser;
intResult = pBAL.UpdatetoShipped(fin);
}
if (intResult > 0)
MessageBox.Show("Record Updated Successfully.");
else
MessageBox.Show("Record couldn't Updated Check Serial");
}
catch (Exception ee)
{
MessageBox.Show(ee.Message.ToString());
}
finally
{
pBAL =null;
}
My problem is it always says updated succussfully. But if i updated it again as duplicate update i want to show serial is already updated.
The key change you need to make is to the following line of SQL from your stored procedure:
UPDATE dbo.FG_FILLIN
SET Status='SHIPPED',
DateModified=#Date,
ModifiedBy=#User,
WorkWeek=#WorkWeek
WHERE (Status='KITTED')
AND SerialNumber=#SerialNumber
You need to return a value that allows you to determine if this UPDATE has already happened or not, for example:
DECLARE #iUpdateAlreadyComplete INT
SET #iUpdateAlreadyComplete = 0;
IF EXISTS
(
SELECT 1
FROM dbo.FG_FILLIN
WHERE Status='SHIPPED'
AND SerialNumber=#SerialNumber
)
BEGIN
SET #iUpdateAlreadyComplete = 1
END
ELSE
BEGIN
UPDATE dbo.FG_FILLIN
SET Status='SHIPPED',
DateModified=#Date,
ModifiedBy=#User,
WorkWeek=#WorkWeek
WHERE (Status='KITTED')
AND SerialNumber=#SerialNumber
END
SELECT #iUpdateAlreadyComplete AS Result
You can then change your DAL from return cmd.ExecuteNonQuery(); to:
var result = Convert.ToInt32(cmd.ExecuteScalar());
return result;
The return value will now be 0 for a record that has been updated, and 1 for one that didn't need updating as it was already processed.
Other Notes
There are a couple of other things that you should consider changing:
sp_UpdatetoShipped is a bad name for a stored procedure. Do not use the sp_ prefix.
Your DAL deliberately catches and re-throws an exception (admittedly in the "best" way), do you really need to?
Rather than explicitly calling Dipose(), use the using() {} syntax instead, as this ensures that Dispose() is called, even in the event of an exception.
using syntax:
using(SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("sp_UpdatetoShipped", conn))
{
}
}
This seems more like a business rule issue than anything to do with an error. What you might want to do is to create a dictionary to hold serial numbers that have already been updated.
e.g.
Dictoinary<string,string> updatedSerialNumbers = new Dictionary<string, string>();
foreach (FillinEntity fin in fillin2)
{
fin.ModifiedBy = loggedUser;
if (updatedSerialNumbers.Contains(fin.SerialNumber) == false)
{
intResult = pBAL.UpdatetoShipped(fin);
updatedSerialNumbers.Add(fin.SerialNumber,fin.SerialNumber);
}
Something like this should sort out your problem.