Limit user to 10 wrong pin code attempts Web Api # - c#

I have been working on a simple web api that accepts a certain phone number and returns a pin code. The idea is that I want to delete/restrict the user to login if pin code is 10 times wrong. Could it be done in SQL Server or in c# code?
All help is appreciated
Validate in business layer:
public void validate(validateRequest vReq, headers head, ref validateResponse vRes)
{
vRes = new validateResponse();
int isCorrect = vRes.isCorrect;
Data dal = new Data();
dal.OpenConnection();
dal.valpinCode(vReq.pinCode, ref isCorrect);
if (isCorrect == 1)
{
vRes.result.code = 0;
vRes.result.message = "Successful";
vRes.isCorrect = 0;
}
else
{
vRes.result.code = -3;
vRes.result.message = "Access Denied.";
vRes.isCorrect = -1;
}
My valpincode in datalayer:
public void valpinCode(string pinCode, ref int isCorrect)
{
try
{
using (IDbConnection db = new SqlConnection(c.connstr))
{
if (db.State != ConnectionState.Open)
db.Open();
DynamicParameters param = new DynamicParameters();
param.Add("#pincode", pinCode);
param.Add("#exist", dbType: DbType.Int32, direction: ParameterDirection.Output);
con.Execute("valpin", param, commandType: CommandType.StoredProcedure);
isCorrect = param.Get<Int32>("#exist");
if (db.State != ConnectionState.Closed)
db.Close();
}
}
catch (Exception ex)
{
Common log = new Common();
log.LogError(ex);
}
}
My valpin SP:
#pincode nvarchar(50),
#exist bit output
AS
BEGIN
IF NOT EXISTS(
SELECT *
FROM Users
WHERE pinCode=#pincode)
BEGIN
SET #exist=0
END
ELSE
BEGIN
SELECT *
FROM Users
WHERE pinCode=#pincode
SET #exist=1
END
END
Users table:
idUser phone countrycode pincode
1 7006100 961 5716
12 7006107 961 77709
note that the pincode is generated randomly

Execute this script just once to add one column and set its value to zero for all users
alter table users
add invalidAttempts int null
;
update users
set invalidAttempts =0
select * from users
This procedure verifies if user does not exist (-1), if user is blocked(-2) if password is wrong (-3) and user and is is Ok (0)
Any invalid attempt is stored in the new field/column. If it's number reaches 10, the user is blocked. You need to establish another method for unblocking it. If the user is not blocked a valid login will reset invalid attempts.
ALTER PROCEDURE valpin #idUser INT, #pincode NVARCHAR(50)
AS
BEGIN
DECLARE #pin VARCHAR(50)
DECLARE #invalidAtempts INT
SELECT #pin = pincode, #invalidAtempts = invalidAttempts
FROM Users
WHERE idUser = #idUser
IF #invalidAtempts IS NULL
RETURN - 1 --User does not exists
END
IF #invalidAtempts >= 10
RETURN - 2 --blocked
IF #pin = #pincode
BEGIN
UPDATE users
SET invalidAttempts = 0
WHERE idUser = #idUser
RETURN 0 -- OK
END
ELSE
BEGIN
UPDATE users
SET invalidAttempts = invalidAttempts + 1
WHERE idUser = #idUser
RETURN - 3 -- wrong ping
END
You need to modify C# code to reflect this behavior. also, instead of using an output parameter use a return value ParameterDirection.ReturnValue;

Related

Why is this stored procedure always return 1 for delete operation?

I tried many solutions but none of them worked so far. This is my first code that uses OUT parameter in the stored procedure.
When I tested the stored procedure in mySQL WB, it works as expected but when I read that in my C# code, it returns 1 always. However, if I hardcode zero for the stored procedure OUT parameter it reads that values in C#. So it is likely stored procedure is not working as expected but cannot figure out why.
Stored procedure :
CREATE DEFINER=`flashLight`#`%` PROCEDURE `DeleteAddress`(IN UserID INT(11),IN AddressID INT(11), OUT AddressDeleted INT(1))
BEGIN
SET #NotLocked = (0);
SET #AddressIDExists = 1;
SET #Deleted = (1);
CALL IsAddressIDInUse(UserID, AddressID, #AddressIDExists);
UPDATE address
SET ADDRESS_DELETED = #Deleted
WHERE
ADDRESS_USER_ID = UserID AND
ADDRESS_ID = AddressID AND
LOCKED = #NotLocked AND
#AddressIDExists = 0;
SET #DeletedRecords = ROW_COUNT();
SET AddressDeleted = IF(#DeletedRecords>0,1,0);
END
Stored procedure test code in mySQL WB
SET #UserID = 160;
SET #AddressID = 229;
SET #AddressDeleted = '';
CALL DeleteAddress(#UserID,#AddressID,#AddressDeleted);
SELECT #AddressDeleted;
AddressDeleted returns correctly (1 or 0) depending on if record deleted or not.
However, if I read it in C# code it returns 1
if (oMySQLConnecion.State == System.Data.ConnectionState.Open)
{
MySqlCommand oCommand = new MySqlCommand("DeleteAddress", oMySQLConnecion);
oCommand.CommandType = System.Data.CommandType.StoredProcedure;
oCommand.Parameters.AddWithValue("#UserID", UserID);
oCommand.Parameters.AddWithValue("#AddressID", AddressID);
MySqlParameter AddressDeleted = new MySqlParameter("#AddressDeleted", MySqlDbType.Int16);
AddressDeleted.Direction = System.Data.ParameterDirection.Output;
oCommand.Parameters.Add(AddressDeleted);
int result = oCommand.ExecuteNonQuery();
result = Convert.ToInt16(oCommand.Parameters["#AddressDeleted"].Value);
if (result > 0)
{
oDBStatus.Type = DBOperation.SUCCESS;
oDBStatus.Message.Add(DBMessageType.SUCCESSFULLY_DATA_DELETED);
}
else
{
oDBStatus.Type = DBOperation.ERROR;
oDBStatus.Message.Add(DBMessageType.ERROR_NO_RECORDS_UPDATED);
}
oMySQLConnecion.Close();
return oDBStatus;
}
As I mentioned, If I hardcode zero to the stored procedure, it returns that value. So it is likely stored procedure is having the issue.

SQL Server validation returns wrong output

I am working on login page with validation on a local server using SQL Server. I created a login page and sign up page, my sign up page works fine but the login page keeps showing an error of "User not activated"
Here is my code behind for loginpage
public partial class Login : System.Web.UI.Page
{
protected void Validate_User(object sender, EventArgs e)
{
int userId = 0;
string constr = `ConfigurationManager.ConnectionStrings["constr"].ConnectionString;`
using (SqlConnection con = new SqlConnection(constr))
{
using (SqlCommand cmd = new SqlCommand("Validate_User"))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Username", Login1.UserName);
cmd.Parameters.AddWithValue("#Password", Login1.Password);
cmd.Connection = con;
con.Open();
userId = Convert.ToInt32(cmd.ExecuteScalar());
con.Close();
}
switch (userId)
{
case -1:
Login1.FailureText = "Username and/or password is incorrect.";
break;
case -2:
Login1.FailureText = "Account has not been activated.";
break;
default:
FormsAuthentication.RedirectFromLoginPage(Login1.UserName, Login1.RememberMeSet);
break;
}
}
}
}
and here is the procedure to validate the user
CREATE PROCEDURE [dbo].[Validate_User]
#Username NCHAR(50),
#Password VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #UserId INT, #LastLoginDate DATETIME
SELECT #UserId = UserId, #LastLoginDate = LastLoginDate
FROM NervSuiteUsers
WHERE Username = #UserName AND [Password] = #Password
IF #UserId IS NOT NULL
BEGIN
IF NOT EXISTS(SELECT UserId FROM NervSuiteUsers WHERE Username = #UserName)
BEGIN
UPDATE NervSuiteUsers
SET LastLoginDate = GETDATE()
WHERE UserId = #UserId
SELECT #UserName [UserName] -- User Valid
END
ELSE
BEGIN
SELECT -2 -- User not activated.
END
END
ELSE
BEGIN
SELECT -1 -- User invalid.
END
END
The problem is even with a user in the database, I still get "Account not Validated"
In addition to glitches in the SP (already discussed), there are problems in the .NET code, associated with whether the result was an integer (failure) or a string (success). One pragmatic way to resolve this would be to always return the same types. Since the user passes in the username, there's not necessarily a huge point in passing it out again, unless your intent is to auto-correct case insensitive strings, but a simple fix would be to simply select 1 (or some other sentinel value) in the success case, instead of select #UserName.
However, the same problem can be fixed in the existing code, simply by testing the value:
object sqlResult = cmd.ExecuteScalar();
switch (sqlResult)
{
case int i when i == -1:
// TODO ...
break;
case int i when i == -2:
// TODO ...
break;
case string s:
// success, and the value was s
// TODO...
break;
default:
// I HAVE NO CLUE
throw new SomeSensibleException(...);
}
Note this uses "new" C# language syntax features, but the same fundamental approach can also be done manually if you're using down-level C#, via:
if (sqlResult is int)
{
switch ((int)sqlResult)
{
// ...
}
}
else if (sqlResult is string)
{
string s = (string)sqlResult;
// ...
}
Your SP makes contradictory statement to me. Below query will give result only when both username/password matches
SELECT #UserId = UserId, #LastLoginDate = LastLoginDate
FROM NervSuiteUsers
WHERE Username = #UserName AND [Password] = #Password
Then this below query, doesn't make sense
IF #UserId IS NOT NULL // will be true when both username/password matches
BEGIN
IF NOT EXISTS(SELECT UserId FROM NervSuiteUsers WHERE Username = #UserName) // Why this???? This will not be TRUE
BEGIN
UPDATE NervSuiteUsers
SET LastLoginDate = GETDATE()
WHERE UserId = #UserId
Thus your else block will gets evaluated and you will get that result you posted
ELSE
BEGIN
SELECT -2 -- User not activated.
END
Apart from all the feedback you have got in comments regarding the issues with the implementation, you have issue with following lines of query.
IF NOT EXISTS(SELECT UserId FROM NervSuiteUsers WHERE Username = #UserName)
BEGIN
UPDATE NervSuiteUsers
SET LastLoginDate = GETDATE()
WHERE UserId = #UserId
SELECT #UserName [UserName] -- User Valid
END
ELSE
BEGIN
SELECT -2 -- User not activated.
END
It should not be NOT EXISTS. It should be IF EXISTS because #UserId NOT NULL mean it exists in the table, change your query like following.
IF EXISTS(SELECT UserId FROM NervSuiteUsers WHERE Username = #UserName)
BEGIN
UPDATE NervSuiteUsers
SET LastLoginDate = GETDATE()
WHERE UserId = #UserId
SELECT #UserName [UserName] -- User Valid
END
ELSE
BEGIN
SELECT -2 -- User not activated.
END

SQL return 2 values to C# web service

I have a SQL Stored Procedure that works like this: it updates the Confirmed column in a PaymentRecords table, which has a default value of 0, to be either 1 (approved) or 2 (rejected). To ensure concurrency control, it prevents updates from occurring if the Confirmed value is already 1 or 2.
When the update is successful, it returns a string 'SUCCESS', where as if it fails, it returns the chequeNumber to display a message saying that particular cheque has already been updated. Both of these are returned to a C# web service.
SQL:
ALTER PROCEDURE [dbo].[UpdateUnverifiedChequeConfirmed]
-- Add the parameters for the stored procedure here
#id uniqueidentifier,
#confirmed int,
#rejectReason nvarchar(MAX),
#bankName nvarchar(MAX)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #existingConfirmed int;
-- Insert statements for procedure here
SELECT TOP 1 #existingConfirmed = [PaymentRecords].[confirmed]
FROM [dbo].[PaymentRecords]
INNER JOIN [dbo].[ChequePayments]
ON [ChequePayments].[paymentRecordID] = [PaymentRecords].[id]
WHERE [ChequePayments].[id] = #id
IF #existingConfirmed = '1' OR #existingConfirmed = '2'
BEGIN
PRINT 'Cheque already updated by another user, further changes not allowed.';
--SELECT 0 AS FAIL
SELECT [ChequePayments].[chequeNo] AS chequeNo
FROM [ChequePayments]
WHERE [ChequePayments].[id] = #id
END
ELSE
BEGIN
UPDATE [dbo].[PaymentRecords]
SET confirmed = #confirmed, rejectReason = #rejectReason, [timestamp] = GETDATE()
FROM [dbo].[PaymentRecords]
INNER JOIN [dbo].[ChequePayments]
ON [ChequePayments].[paymentRecordID] = [PaymentRecords].[id]
WHERE [ChequePayments].[id] = #id
UPDATE [dbo].[ChequePayments]
SET bankName = #bankName, [timestamp] = GETDATE()
WHERE [ChequePayments].[id] = #id
--SELECT 1 AS SUCCESS
SELECT 'SUCCESS' AS SUCCESS
END
END
C#:
unverifiedChequeNumberResponse ICommon.updateUnverifiedCheque(unverifiedChequeRequest request, string id)
{
string result, rejectReason;
Guid newID;
if (request.bankName == "Select a Bank")
{
return new unverifiedChequeNumberResponse("Error: No bank selected.");
}
if (request.rejectReason == "")
{
rejectReason = null;
}
else
{
rejectReason = request.rejectReason;
}
if (Guid.TryParse(id, out newID))
{
try
{
result = dbkl.UpdateUnverifiedChequeConfirmed(newID, request.confirmed, rejectReason, request.bankName).FirstOrDefault();
}
catch (Exception ex)
{
if (isDebug() == true)
{
return new unverifiedChequeNumberResponse(ex.Message);
}
else
{
return new unverifiedChequeNumberResponse("Error: Database inaccessible");
}
}
if (result == "SUCCESS")
{
return new unverifiedChequeNumberResponse();
}
else
{
unverifiedChequeNumberResponse response = new unverifiedChequeNumberResponse();
response.chequeNo = result;
return new unverifiedChequeNumberResponse("Error: Cheque " + response.chequeNo + " already updated by another user.");
}
}
else
{
return new unverifiedChequeNumberResponse("Error: Invalid ID.");
}
}
Although all of this is working properly, I need to make a change so that the SP returns a 1 when successful and 0 when unsuccessful. The two issues I am facing are:
How to make the unsuccessful update path in the SP return 2 values, namely 0 and the chequeNumber? If I write it as:
SELECT 0 AS FAIL, [ChequePayments].[chequeNo] AS chequeNo
FROM [ChequePayments]
WHERE [ChequePayments].[id] = #id
it ends up returning a null value and throws an exception.
If the chequeNumber has a value of 1 (unlikely in practice but still worth covering) and the update is denied, the web service will still return the success message. Is there a way to change it so that the service can differentiate between 1 as a chequeNumber and 1 as a Success value?
You could look at using SQL Output parameters. For example;
ALTER PROCEDURE [dbo].[UpdateUnverifiedChequeConfirmed]
-- Add the parameters for the stored procedure here
#id uniqueidentifier,
#confirmed int,
#rejectReason nvarchar(MAX),
#bankName nvarchar(MAX),
#result1 int output,
#result2 varchar(50) output
AS
BEGIN
-- do stuff...
set #result1 = 123
set #result2 = 'success'
END
You would have to update your .Net code to define the output paramaters then read their values after the call.
You might also want to consider using transactions for concurrency control - that's what they're for, and they will provide a more robust solution than you have here.

SQL Server and how to handle the type of error without conflict

I use SQL Server 2008 and ASP.NET C# and I have a stored procedure that returns some calculated rows.
I want to check the parameters values before doing the process and I want to return a custom error message if the parameters values are wrong.
In the UI side, I must return a different text and use a different window according to the error type. Example:
Errors when the parameters values are wrong.
Non handled errors.
I currently use this kind of SP in the database, but I tried to use the state argument and even the severity argument to identify the type of error. But I have some conflicts with non handled error returning the same state number than my parameter error, and so the wrong window comes up. If I use the severity level, I guess the SP could return an error with the same severity number as well in some cases.
I give you a quick example to have a better view:
CREATE PROCEDURE dbo.GetData
#Date1 date,
#Date2 date
AS
BEGIN
-- check the parameters
IF #Date2 < #Date1
BEGIN
RAISERROR(N'Date 2 cannot be less than Date 1', 16, 2); -- <= Here State 2
return
END
-- process here...
DECLARE #Table1 TABLE ( name nvarchar(50) NOT NULL)
-- Supposing you have to insert a row with a NULL value
INSERT INTO #Table1 VALUES (NULL);
-- Thus, this query returns this error with the state 2 as well!
--Msg 515, Level 16, State 2, Procedure GetData, Line 21
--Cannot insert the value NULL into column 'name', table '#Table1'; column does not allow nulls. INSERT fails.
SELECT 'Record 1';
SELECT 'Record 2';
END
From c#:
List<string> data = new List<string>();
protected void Button1_Click(object sender, EventArgs e)
{
string errorMessage = string.Empty;
bool isErrorFromChecking = false;
if (GetValues(ConfigurationManager.ConnectionStrings["TestConnectionString"].ConnectionString,
new DateTime(2011, 01, 01), new DateTime(2011, 02, 01),
ref isErrorFromChecking, ref errorMessage))
{
Label1.Text = data[0].ToString();
return;
}
if (isErrorFromChecking)
{
Label1.Text = errorMessage;
return;
}
Label1.Text = string.Format("Internal Error: {0}.", errorMessage);
}
private bool GetValues(string connectionString, DateTime date1, DateTime date2,
ref bool isErrorFromChecking, ref string errorMessage)
{
data = new List<string>();
try
{
using (SqlConnection sqlCon = new SqlConnection(connectionString))
{
sqlCon.Open();
SqlCommand sqlCmd = new SqlCommand();
sqlCmd.Connection = sqlCon;
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.CommandText = "dbo.GetData";
sqlCmd.Parameters.AddWithValue("Date1", date1);
sqlCmd.Parameters.AddWithValue("Date2", date2);
SqlDataReader reader = sqlCmd.ExecuteReader();
while (reader.Read())
{
data.Add(reader[0].ToString());
}
reader.Close();
sqlCon.Close();
}
}
catch (SqlException ex)
{
if (ex.State == 2)
{
isErrorFromChecking = true;
errorMessage = ex.Message;
return false;
}
isErrorFromChecking = false;
errorMessage = ex.Message;
return false;
}
catch (Exception ex)
{
isErrorFromChecking = false;
errorMessage = ex.Message;
return false;
}
return true;
}
In the code above, the dates are correct but the program does not return the message “Internal Error: …” although the SP had an error.
I have some ideas, but I just want to know your point of view and the best way to do it.
Thanks.
According to this http://msdn.microsoft.com/en-us/library/ms178592.aspx you will get a message id of 50000 when you provides the message string yourself. I guess you could test for that in your C# code.
Or you could specify a MsgID (Value > 50000) in the SP instead and provide the error message based on the MsgID in the client code.
Simply split the logic in to two. By this I mean have the 'parameter check' logic in one Stored Procedure and the rest in another SP.
In the 'check' SP use output parameters to get any codes or error text you want returned.
The second SP can also call SP first to ensure it's not getting sent bad data.
The C# should then call the 'check' SP first and see what is returned.
Alternative is to use an output parameter for the error condition and return an empty dataset.
CREATE PROCEDURE spTest (#param1 int, #ErrorText varchar(50) OUTPUT )
AS
BEGIN
SET #ErrorText = NULL
IF #Param1 = 1
BEGIN
SET #ErrorText = 'I really cant except 1 as a parameter'
SELECT NULL as QueryPlaceholderCol
END
ELSE
BEGIN
SELECT 3.141 AS QueryPlaceholderCol
END
END
To test in T-SQL:
DECLARE #ReturnedError VARCHAR(50)
EXEC spTest 1,#ReturnedError OUTPUT
SELECT CASE WHEN #ReturnedError IS NULL THEN 'Worked Fine' ELSE #ReturnedError END AS Outputs
EXEC spTest 1423,#ReturnedError OUTPUT
SELECT CASE WHEN #ReturnedError IS NULL THEN 'Worked Fine' ELSE #ReturnedError END AS Outputs

ExecuteNonQuery on a stored proc causes it to be deleted

This is a strange one. I have a Dev SQL Server which has the stored proc on it, and the same stored proc when used with the same code on the UAT DB causes it to delete itself!
Has anyone heard of this behaviour?
SQL Code:
-- Check if user is registered with the system
IF OBJECT_ID('dbo.sp_is_valid_user') IS NOT NULL
BEGIN
DROP PROCEDURE dbo.sp_is_valid_user
IF OBJECT_ID('dbo.sp_is_valid_user') IS NOT NULL
PRINT '<<< FAILED DROPPING PROCEDURE dbo.sp_is_valid_user >>>'
ELSE
PRINT '<<< DROPPED PROCEDURE dbo.sp_is_valid_user >>>'
END
go
create procedure dbo.sp_is_valid_user
#username as varchar(20),
#isvalid as int OUTPUT
AS
BEGIN
declare #tmpuser as varchar(20)
select #tmpuser = username from CPUserData where username = #username
if #tmpuser = #username
BEGIN
select #isvalid = 1
END
else
BEGIN
select #isvalid = 0
END
END
GO
Usage example
DECLARE #isvalid int
exec dbo.sp_is_valid_user 'username', #isvalid OUTPUT
SELECT valid = #isvalid
The usage example work all day... when I access it via C# it deletes itself in the UAT SQL DB but not the Dev one!!
C# Code:
public bool IsValidUser(string sUsername, ref string sErrMsg)
{
string sDBConn = ConfigurationSettings.AppSettings["StoredProcDBConnection"];
SqlCommand sqlcmd = new SqlCommand();
SqlDataAdapter sqlAdapter = new SqlDataAdapter();
try
{
SqlConnection conn = new SqlConnection(sDBConn);
sqlcmd.Connection = conn;
conn.Open();
sqlcmd.CommandType = CommandType.StoredProcedure;
sqlcmd.CommandText = "sp_is_valid_user";
// params to pass in
sqlcmd.Parameters.AddWithValue("#username", sUsername);
// param for checking success passed back out
sqlcmd.Parameters.Add("#isvalid", SqlDbType.Int);
sqlcmd.Parameters["#isvalid"].Direction = ParameterDirection.Output;
sqlcmd.ExecuteNonQuery();
int nIsValid = (int)sqlcmd.Parameters["#isvalid"].Value;
if (nIsValid == 1)
{
conn.Close();
sErrMsg = "User Valid";
return true;
}
else
{
conn.Close();
sErrMsg = "Username : " + sUsername + " not found.";
return false;
}
}
catch (Exception e)
{
sErrMsg = "Error :" + e.Source + " msg: " + e.Message;
return false;
}
}
Ok, I have found the answer ... simple when you know how!
I saw this link here :
Disappearing Stored Procedure
Disappearing Stored Procedure
So from the best answer in that I ran :
select syo.name
from syscomments syc
join sysobjects syo on
syo.id = syc.id
where syc.[text] like '%DROP PROC%'
This gave me one of my OTHER stored procs back... sp_is_user_admin, which didn't seem right so I had a quick look ...
create procedure dbo.sp_is_user_admin
#username as varchar(20),
#isadmin as int OUTPUT
AS
BEGIN
declare #profile as varchar(20)
select #profile = profile from CPUserData where username = #username
if #profile = 'admin'
BEGIN
select #isadmin = 1
END
else
BEGIN
select #isadmin = 0
END
END
--*********************************************************************************
-- Check if user is registered with the system
IF OBJECT_ID('dbo.sp_is_valid_user') IS NOT NULL
BEGIN
DROP PROCEDURE dbo.sp_is_valid_user
IF OBJECT_ID('dbo.sp_is_valid_user') IS NOT NULL
PRINT '<<< FAILED DROPPING PROCEDURE dbo.sp_is_valid_user >>>'
ELSE
PRINT '<<< DROPPED PROCEDURE dbo.sp_is_valid_user >>>'
END
Doh!!! There is the blighter... in the C# what happens is that if the user is valid I also choose what to let them see based on if they are admin or not and calling that was blitzing the sp_is_valid_user proc. Nasty side effect!
// check the user is entitled to use the system at all
if (usrData.IsValidUser(sCurrentUserName, ref sErrMsg))
{
// if the user is admin then let them spoof and edit their own data
if (usrData.UserIsAdmin(sCurrentUserName, ref sErrMsg))
{
chkSpoof.Visible = true;
grdvwUserDataFromDB.Visible = true;
}
}
else
{
// redirect them away
Response.Redirect("UserNotRegistered.aspx");
return;
}
I hope this helps someone else out!
PS: DB Artisan is nasty and if I had the full fat SQL Server available in my Development toolkit then I guess I could have used the profiler to see this being called. ;P I can't install SQL Server 2008 as I don't have the right SP / updates to Visual Studio I think and IT here can't sort it out, annoying!!

Categories