|
Kevin Marois wrote: a WPF app
Whatever that is.
I try to stay in the backend, plus console apps as needed, and WinForms when appropriate.
You'll never get very far if all you do is follow instructions.
|
|
|
|
|
Kevin Marois wrote: If you put a TRY/CATCH in the DAL, log the error, then what..??? Throw again and
catch it again in a UI side TRY/CATCH? No, don't ever add code that does nothing or adds nothing.
Kevin Marois wrote: What about situations where it's NOT an exception, but the UI needs to know
something, like a duplicate record problem A duplicate record simply is an exception.
Kevin Marois wrote: So far this works well, but I'd like to get your opinion. Do you have a better
way? No, I don't.
I prefer exceptions over return-values, as they force the programmer to anticipate in handling them, whereas returnvalues can and will be ignored. Then some wiseguy came up with the idea to ignore exceptions as well.
Oh, well.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
The problems I'm trying to address are
1) Try/Catch blocks All Over.
You always want try/catch's wrapping your DAL data methods. If you rely on catching exceptions in the UI, then other apps that use the DAL might not handle then properly and you open the door to corrupted data or unhandled exceptions.
When I see a method header like
void SomeFunction();
I don't know what's happening in the method, weather it's handling exceptions or not. But if I see this:
Response<SomeEntity> SomeFunction();
and I look at the Response and see an Exception property, then I can reasonably know that the method is handling exceptions in the DAL. I would still need to check the exception, but it might NOT be an exception. It could simply be some message suck as "Customer credit limit exceeded".
2) Provide a means to communicate other issues to the caller.
I do not agree that a duplicate record is an exception. First, it really is an issue of the business rules. Second, the user types in "Blah" and saves, and then "Blah" again and saves, and the app should log & throw?? Really? The app should notify the user, but no other action is necessary.
3). Provide a single point of handling problems, and a single method for returning info to the caller. There will most certainly be other applications accessing this data via the API. Since I don't know right now what they are, setting up the DAL to return responses instead of just throwing seems logical because I'm not relying on some other app or person to handle it. The DAL handles it and the programmer can do whatever they want with the response.
Just my 2 cents.
If it's not broken, fix it until it is
|
|
|
|
|
Kevin Marois wrote: 1) Try/Catch blocks All Over. Yup, those are a problem. If you don't intend to do something with it (logging should be done at the least amount of levels required) then don't write a handler. Let it bubble up. Don't add yet another empty handler.
Kevin Marois wrote: You always want try/catch's wrapping your DAL data methods Even if it is a Win32Exception whose error-message I can get, it is usually something the end-user can handle, not I. The options are often "try again" or "fail".
But then again, I don't do generic exception handling in a DAL-layer. If there's a disk-full message, then it bubbles up, is displayed, and can only be dismissed. The action can be tried again (by the user) after that oc. If it cannot be retried automatically, then there's no local handler, and the threads' exception handler will take care of it. There's sldo no need to catch-and-rethrow some validation exception, that's the responsibility of the calling code.
Kevin Marois wrote: I do not agree that a duplicate record is an exception. First, it really is an
issue of the business rules. You do not have to; it's not the BR-rules that define how the data is saved most efficiently. There technically cannot be "duplicate" records in a relational database. If your BR require for "duplicates", I'll add an identity-field (an artificial key in normalization terms) to distinguish between both entities. The identity would be the PK, and there'd be a unique key on the things you use to distinguish between records to identify (and if need be, notify) of breach of such a BR-constraint.
Kevin Marois wrote: Second, the user types in "Blah" and saves, and then "Blah" again and saves, and
the app should log & throw?? Really? The app should notify the user, but no
other action is necessary. It depends on the case. If it is a unique constraint, and a new baby gets an existing social security number, then you might want to throw an exception stating merely "computer says no". I do not know al the reasons why there cannot exist two identities with the same unique identifying key; only the user knows - and sometimes the dev has to remind the user he/she doesn't know either.
[Serializable]
public class ComputerSaysNoException : Exception
{
public ComputerSaysNoException() { }
public ComputerSaysNoException( string message ) : base( message ) { }
public ComputerSaysNoException( string message, Exception inner ) : base( message, inner ) { }
protected ComputerSaysNoException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context ) : base( info, context ) { }
}
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Eddy Vluggen wrote: You do not have to; it's not the BR-rules that define how the data is saved most efficiently. There technically cannot be "duplicate" records in a relational database
Agreed.. the BL doesn't have anything to do with HOW the data is saved. It's concerned with WHAT data is saved.
You're confusing a Business Rule Violation with an Exception. What is an exception? It's simply a fancy word for Error. A BR violation isn't an error. It's a condition that can't be allowed to continue on to the DAL.
An example would be a customer's credit limit being reached. The DAL never gets called because the BL determined that a business rule would be violated if it continued.
And to say "There technically cannot be "duplicate" records in a relational database" isn't always true. Again, this is something determined by the app requirements.
My point in all this is that I think I've come up with a standardized way of returning to the call ANYTHING that happens, whether it's a BL violation or an exception.
If it's not broken, fix it until it is
|
|
|
|
|
Kevin Marois wrote: You're confusing a Business Rule Violation with an Exception. No, I just want to force the caller to handle the violation.
Kevin Marois wrote: What is an exception? It's simply a fancy word for Error. A BR violation isn't
an error. It's a condition that can't be allowed to continue on to the DAL. It's a condition that needs handling outside of the conventional expected routine, which is to 'crud' something.
Kevin Marois wrote: And to say "There technically cannot be "duplicate" records in a relational
database" isn't always true. Again, this is something determined by the app
requirements. Codd[^] says no. It's determined by the abstraction. The DAL is there so BO doesn't have to worry about these details; if it is a BR, it will be implemented (often at multiple levels), and if possible, handled.
Think of the not-null constraint. It'll be checked in the browser, to save a trip to the server. It'll be checked on the server, to prevent accidents. It'll be checked again in the database, tripping the WinForm/WPF guy/gal.
Kevin Marois wrote: My point in all this is that I think I've come up with a standardized way of
returning to the call ANYTHING that happens Just be sure to check the return-code and not simply ignore it
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
I was hoping that the following would work, but I get "Argument 1: cannot convert from 'ref string' to 'ref object'" and
"Argument 1: cannot convert from 'ref int' to 'ref object'" error messages. Anyone know how I can solve this?
public void Test()
{
string MyString = "";
int MyInt = 0;
GetTypeAndSetToOne(ref MyString);
GetTypeAndSetToOne(ref MyInt);
}
public void GetTypeAndSetToOne(ref object Value)
{
if (Value is int)
{
Value = 1;
Console.WriteLine("int");
}
else if (Value is string)
{
Value = "1";
Console.WriteLine("string");
}
}
|
|
|
|
|
When you pass a parameter by reference, the type of the variable you pass in has to match the type of the parameter exactly. Otherwise, you could write:
public void GetTypeAndSetToOne(ref object Value)
{
Value = new Frood("Towel");
}
...
int MyInt = 0;
GetTypeAndSetToOne(ref MyInt);
To make your code work, you'll need to box the variable into a temporary object variable, pass the temporary variable to the method, and then un-box the temporary variable back into the real variable:
string MyString = "";
int MyInt = 0;
object temp = MyString;
GetTypeAndSetToOne(ref temp);
MyString = (string)temp;
temp = MyInt;
GetTypeAndSetToOne(ref temp);
MyInt = (int)temp;
It's messy code, and liable to break if your method changes. If you explain what you're really trying to achieve, we might be able to suggest a better approach.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Snap!
Those who fail to learn history are doomed to repeat it. --- George Santayana (December 16, 1863 – September 26, 1952)
Those who fail to clear history are doomed to explain it. --- OriginalGriff (February 24, 1959 – ∞)
|
|
|
|
|
What is it they say about "great minds"?
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
There is no "nice" way - ref parameters must be of the same exact type as the value being passed, and the value must be an assignable value, so casts aren;t allowed either. (This is to ensure type integrity: otherwise if you assigned a new instance of a class in the method, it could end up with a "bad" class
class A {}
class B : A {}
class C : A {}
...
B b = new B();
Change(ref b);
...
void Change(ref A a)
{
a = new C();
}
You can do it, but you have to work with the system, and faff about a bit:
public void Test()
{
string MyString = "";
int MyInt = 0;
object oS = MyString;
GetTypeAndSetToOne(ref oS);
MyString = (string) oS;
object oI = MyInt;
GetTypeAndSetToOne(ref oI);
MyInt = (int) oI;
}
Those who fail to learn history are doomed to repeat it. --- George Santayana (December 16, 1863 – September 26, 1952)
Those who fail to clear history are doomed to explain it. --- OriginalGriff (February 24, 1959 – ∞)
|
|
|
|
|
Perhaps you want to use overloading?
You'll never get very far if all you do is follow instructions.
|
|
|
|
|
Generic could make it look like it works, with some boxing as suggested earlier in this thread. It's not a very elegant solution though.
using System;
namespace ConsoleApplication5
{
class Program
{
static void GetTypeAndSetToOne<T>(ref T Value)
{
if (Value is int)
{
Value = (T)(object)1;
Console.WriteLine("int");
}
else if (Value is string)
{
Value = (T)(object)"1";
Console.WriteLine("string");
}
}
static void Main(string[] args)
{
string myString = "";
int myInt = 0;
GetTypeAndSetToOne(ref myString);
GetTypeAndSetToOne(ref myInt);
Console.WriteLine("MyString, type = {0}, value = {1}", myString.GetType(), myString);
Console.WriteLine("MyInt, type = {0}, value = {1}", myInt.GetType(), myInt);
Console.ReadLine();
}
}
} Since MyString and MyInt are both local, I changed the capitalization of the starting-letter to indicate that. Otherwise it'd be suggesting it's a property, or at least a public field.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
In SQL Server 2005 Stored Procedure I have the following:
[CODE]
CREATE FUNCTION [dbo].[LISTSTAFF]()
RETURNS TABLE
AS
RETURN(select * from tblStaff);
[/CODE]
In C#2005 I assign staff style table for gridview an error, can you help me fix this ?
[CODE]
...
SqlCommand cmd = new SqlCommand("LISTSTAFF", cnn);
cmd.CommandType = CommandType.StoredProcedure;
gridview.DataSource = cmd.ExecuteReader();// waring error in here
...
[/CODE]
I want assign list database style table for gridview.DataSource =... how assign ?
|
|
|
|
|
What is the error message?
Those who fail to learn history are doomed to repeat it. --- George Santayana (December 16, 1863 – September 26, 1952)
Those who fail to clear history are doomed to explain it. --- OriginalGriff (February 24, 1959 – ∞)
|
|
|
|
|
error message when runtime: "The request for procedure 'LISTSTAFF' failed because 'LISTSTAFF' is a tables valued function object"
|
|
|
|
|
Yes - because it is a function, not a procedure.
And you don't call functions directly, you call them via a SELECT or from a SP.
You might find that using a DataAdapter rather than a DataReader works better as well.
Try this:
using (SqlConnection cnn = new SqlConnection(strConnect))
{
cnn.Open();
using (SqlCommand cmd = new SqlCommand("SELECT * FROM LISTSTAFF()", cnn))
{
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
da.Fill(dt);
gridview.DataSource = dt;
}
}
}
Those who fail to learn history are doomed to repeat it. --- George Santayana (December 16, 1863 – September 26, 1952)
Those who fail to clear history are doomed to explain it. --- OriginalGriff (February 24, 1959 – ∞)
|
|
|
|
|
if I use the DataSet or DataTable through SQL queries and assign the result to gridview.DataSource my program running very good but I want to learn writing Stored Procedures, Stored procedure writing I heard the program will run faster. you did not understand my question
|
|
|
|
|
That's because you asked the wrong question!
What you have created is an SQL Function, not an SQL Stored Procedure, and the two are very different things.
Stored Procedures are designed to be used from inside or outside SQL, while Functions are meant to be internal - it's like the difference between a class and it's private methods.
So delete your function (or there will be problems as the name is in use) and create this as a Stored Procedure instead of a Function:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE LISTSTAFF
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM tblStaff
END
GO And your original code will give no error.
It won't display anything, either, probably - since the Connection will need to be open while the gridview loads from the reader and that's bad practice. I'd use the DataAdapter way if you want a DataSource rather a DataReader.
Those who fail to learn history are doomed to repeat it. --- George Santayana (December 16, 1863 – September 26, 1952)
Those who fail to clear history are doomed to explain it. --- OriginalGriff (February 24, 1959 – ∞)
|
|
|
|
|
I think the procedure can not be used because the procedure does not return a value and assign that value into gridview.DataSource. If you say the procedure is used, you can post your templates or my examples of how you edit ?
|
|
|
|
|
Member 2458467 wrote: procedure does not return a value
Um.
How do I put this? That turns out not to be the case...
A stored procedure is a collection of SQL statements, and so acts just as if you had submitted the statements as your SQL Command. So if you use SELECT, it "returns" the selected rows in just the same was as if you had used the SELECT statement directly instead of executing the stored procedure. Except it accepts parameters better, in that it works with OUTPUT parameters which it can set to specific values are return to your code via the Parameters collection of the SqlCommand in your C# code.
Trust me on this: Stored Procedures are used to "simplify" complicated SQL commands all the time: and that means returning result sets!
Try it: you will see what I mean. (But use a DataAdapter instead of a DataReader: you need the whole information available in order to use a DataSource correctly).
Those who fail to learn history are doomed to repeat it. --- George Santayana (December 16, 1863 – September 26, 1952)
Those who fail to clear history are doomed to explain it. --- OriginalGriff (February 24, 1959 – ∞)
|
|
|
|
|
Thank you for help me, I did it.
|
|
|
|
|
You're welcome!
Those who fail to learn history are doomed to repeat it. --- George Santayana (December 16, 1863 – September 26, 1952)
Those who fail to clear history are doomed to explain it. --- OriginalGriff (February 24, 1959 – ∞)
|
|
|
|
|
I've never called a function from ADO.NET, at least not directly, so I've learned something today! You need to mod what I'm about to say next through my lack of knpowledge:
Anyway when looking into this this SO post[^] seemed relevant - you need to add a parameter for the return value.
I don't know why you are creating a function (CREATE FUNCTION [dbo].[LISTSTAFF]() ) rather than an stored proc, but you'd more than likely need to assign the output parameter required to the data source rather than the result of the execute statement? I actually don't know on this part because as I say I've never really used a function before.
The other thing to do is take a step back and look at what you are doing - using a function in this context is odd, you are selecting a whole table, so I'd assume an stored procedure would be better (or even possibly querying a view depending on your requirements).
|
|
|
|
|
Try executing a "SELECT * FROM [LISTSTAFF]".
Should simply not be wrapped in a function. Shouldn't even be wrapped in a sproc, unless there's a reason to (something that adds value for either you or the enduser).
Member 2458467 wrote: gridview.DataSource =... how assign ? Try the manual[^], you're not the first to try and load data in a list.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|