Personally, I have never ran into that problem because I will normally throw an error on "DataPortal_Fetch" if the ID is invalid.
In my way, in the client app I simply catch the error and do whatever I need to do to handle the situation. For me, this is more intuitive.
Throwing an excepiton is cheap. Catching exceptions is "expensive".
I put that in quotes, because it is all relative. Are you talking to a database? Are you using a remote data portal? If you answer 'yes' to either question, then the overhead of those activities is so great that there's no comparative overhead to catching a single exception.
Seriously, this "catching exceptions is expensive" thing gets blown way out of proportion. Yes, compared to a method call, catching an exception is expensive - so you shouldn't throw tons of exceptions in tight loops.
But they aren't that expensive - especially in a case like this, where you've already incurred overwhelming overhead by talking to a database and/or an app server.
Keep in mind that you can use the exception-based scheme (which is how CSLA is designed) to get the no-data result back from DP_F, and then hide that in your factory. Your factory can be:
public static EnterpriseUser GetUser(...)
{
try
{
return DataPortal.Fetch<EnterpriseUser>(new Criteria(...));
}
catch
{
return null;
}
}
The caller gets the semantics you desire, but the overall architecture supported by CSLA remains intact.
That's exactly the code I use. For example,
using (SafeDataReader dr = new SafeDataReader(cmd.ExecuteReader()))
{
if (dr.Read())
PopulateMembers(dr);
else
throw new RecordNotFoundException(id.ToString());
}
*Every* root object fetch method I have is retrieved by ID (I use Guids). If the record can't be found, I definitely consider it an exception. Since I'm using a custom exception, though, I can handle the situtation a bit more gracefully in the UI as opposed to handling more generic exceptions.
In the instance where you're looking up a customer by SSN (for example), what I typically do is pull back a list of customers who have an SSN that match the user-entered criteria. If the list only returns one item - I immediately turn around and retrieve the root object and display the appropriate lookup/edit screen. If the list returns more than one item, I display a search results form where the user can pick from the returned result list.
tchimev:I want to return null object when calling Fetch.
But the exception does not bubble up to the static GetUser(id) method, it says 'Exception was unhandled'...
I wanted to do the same thing, so I just have my static Get__() method test for a null/default object key and return null rather than the empty object instead of throwing an exception.
The debugger will often consider an exception to be "unhandled" when it crosses dynamic invocation boundaries. CSLA uses dynamic invocation (or reflection in older versions) to call the DataPortal_XYZ methods, and the debugger doesn't properly (imo) handle this scenario.
One solution is to click Debug|Exceptions and then deselect the option for managed user exceptions - thus preventing the debugger from breaking on unhandled exceptions like this.
That turns out to be pretty much a requirement when working in Silverlight - at least for unit testing, because most unit test frameworks (including UnitDriven) dynamically invoke the test methods, and many tests have expected (but "unhandled") exceptions...
I ran into this issue today and wanted to weigh in on the discussion. This technique uses exception handling as a control structure, which is generally not a good idea. As such, I implemented the exception handling as described since it came from Rocky, and have DataPortal_Fetch throw MyException accordingly. However, the exception received in the factory method contains this hierarchy:
DataPortalException -> CallMethodException -> MyException
Given the principle of "only catch exceptions that you can do something with", I'm interested in catching only MyException, allowing the rest to bubble up the stack (since they really are true exceptions). This means carefully interrogating the exception hierarchy in each factory method that needs to exhibit this behavior. :( In all, it seems like a lot of work for simply having the factory method return null when no data was found.
There's got to be a better way.
The problem is that there's (potentially) a network boundary between the server-side and client-side code. The most important thing you get from DataPortalException is that it brings the server-side stack trace and exception hierarchy back to the client. That's not normal - that's something CSLA is doing for you via DataPortalException.
If you are OK with losing all that information, you could, on the client side, catch DataPortalException and then throw ex.BusinessException - which means the client would only see the original exception - without the full stack trace, etc.
At runtime maybe that is OK, but at development time it would be really hard to debug some problems without access to the full exception information - I know this because early versions of CSLA didn't have DataPortalException, and it made debugging extremely challenging...
Thanks for the reply, Rocky.
Catching DataPortalException and inspecting .BusinessException is definitely reasonable. I'm going to adopt that strategy from now on.
FWIW, here's the catch block I'm using:
Friend Shared Function GetFoo(ByVal id As Guid) As Foo
Try
Return DataPortal.Fetch(Of Foo)(New Criteria(id))
Catch ex As DataPortalException
If TypeOf ex.BusinessException Is ApplicationException Then
Return Nothing
Else
Throw
End If
End Try
End Function
Oh, but you can use a cool VB feature if you'd like. The Catch statement allows a When clause in VB:
Catch ex As DataPortalException When ex.BusinessException Is ApplicationException
(that's from memory - but it works something like that)
This is one of the coolest features of VB imo
I achieved the same thing as follows:
I declared a variable
private bool _objectExists = false;
Then set this variable true inside DataPortal_Fetch as follows:
if (dr != null && dr.Read())
{
_objectExists = true;
}
Rewrote static method as follows:
var obj = DataPortal.Fetch<Category>(new Criteria(personid));
if (!obj._objectExists)
{
obj = null;
}
return obj;
Thanks
simi
Copyright (c) Marimer LLC