Capture exception thrown by Hystrix fallback? - spring-cloud

I'm designing a service facade and I have a method signature that looks like this:
public Policy getPolicy(long policyId) throws PolicyNotFoundException
If nothing bad happens then a Policy object (simple POJO) is returned. If the requested policy is not found then a checked exception PolicyNotFoundException is thrown (just as a reference - we follow this article when it comes to best practices on exception handling within an application).
The layer above the service facade layer (in this case a Spring MVC RestController) knows how to handle such a PolicyNotFoundException and return an appropriate payload.
I'm trying to incorporate this into a HystrixCommand by doing something like this:
#HystrixCommand(groupKey = "PolicyService", fallbackMethod = "getPolicySafe", ignoreExceptions = { PolicyNotFoundException.class })
public Policy getPolicy(long policyId) throws PolicyNotFoundException {
LOGGER.info("Getting policy {}", policyId);
// Simulate some error condition for testing purposes
throw new RuntimeException("Something happened!");
}
private Policy getPolicySafe(long policyId, Throwable t) throws PolicyNotFoundException {
LOGGER.warn("Falling back to circuit-breaker for getting policy {}", policyId, t);
throw new PolicyNotFoundException(policyId);
}
Basically I want my circuit breaker to simply behave as if the policy wasn't found by the original lookup. The problem I'm having with this though is the exception I throw from the fallback method is getting lost in translation somewhere. The exception I end up seeing in the layer above is the RuntimeException thrown by the command method and not the exception thrown by the fallback method. Is there a way around this? I don't want to change the contract of my original method either nor do I want the layer above this to know anything other than to have to catch PolicyNotFoundException in the case a policy isn't found. Whatever is needed here should be captured within this service facade layer.
Any and all help would be greatly appreciated. Thanks!

So based on the link #spencergibb gave - I may have found a solution after upgrading to Hystrix 1.5.7. This code works as expected
PolicyRestController.java
#RestController
#RequestMapping("/policies")
public class PoliciesApi {
private static final Logger LOGGER = LoggerFactory.getLogger(PoliciesApi.class);
#Autowired
private PolicyService policyService;
#RequestMapping(value = "/{policyId}", method = RequestMethod.GET, produces = { MediaTypes.POLICY_JSON_VALUE, MediaTypes.POLICY_XML_VALUE })
public Policy getPolicy(#PathVariable long policyId) {
try {
// This just shown for simplicity. There is more to this method (input validation/etc)
return this.policyService.getPolicy(policyId);
}
catch (PolicyNotFoundException ex) {
// NotFoundException is a RuntimeException annotated with #ResponseStatus(HttpStatus.NOT_FOUND)
// So the service returns a 404 to the client
LOGGER.info("Policy {} wasn't found", ex.getPolicyId(), ex);
throw new NotFoundException(String.format("Policy %s was not found", ex.getPolicyId()));
}
}
}
PolicyService.java
public interface PolicyService {
#Cacheable("allPolicies")
public List<Policy> getPolicies();
#Cacheable("policies")
public Policy getPolicy(long policyId) throws PolicyNotFoundException;
}
PolicyServiceImpl.java:
#Service
public class PolicyServiceImpl implements PolicyService {
#HystrixCommand(groupKey = "PolicyService", fallbackMethod = "getPolicySafe", ignoreExceptions = { PolicyNotFoundException.class })
public Policy getPolicy(long policyId) throws PolicyNotFoundException {
LOGGER.info("Getting policy {}", policyId);
// Simulate some error condition for testing purposes
throw new RuntimeException("Something happened!");
}
#HystrixCommand(groupKey = "PolicyService", ignoreExceptions = { PolicyNotFoundException.class }, raiseHystrixExceptions = { HystrixException.RUNTIME_EXCEPTION })
private Policy getPolicySafe(long policyId) throws PolicyNotFoundException {
// Here is we hit our fallback we want to log a warning & simply act as if the policy wasn't found by throwing the same contingency exception as the API does
LOGGER.warn("Falling back to circuit-breaker for getting policy {}", policyId);
throw new PolicyNotFoundException(policyId);
}
}

While your solution might work for you I've noticed some weirdness in your code (I can't check my assumptions so I would like to ask you to check this).
Try to avoid using checked exceptions in your code because it's
awkward to maintain.
Based on your code you will never catch
"PolicyNotFoundException" since you're using raiseHystrixExceptions = { HystrixException.RUNTIME_EXCEPTION } which means that you won't to get your custom exception so that HystrixRuntimeException will be propagated. Try to rewrite your code as follows so it should
simplify the code and maybe fix some of your problems:
#Service
public class PolicyServiceImpl implements PolicyService {
#HystrixCommand(groupKey = "PolicyService", fallbackMethod = "getPolicySafe")
public Policy getPolicy(long policyId) throws PolicyNotFoundException {
LOGGER.info("Getting policy {}", policyId);
throw new PolicyNotFoundException(); // throw real PolicyNotFoundException if policy is absent for the given id
}
#HystrixCommand(groupKey = "PolicyService")
private Policy getPolicySafe(long policyId) throws PolicyNotFoundException {
// Here is we hit our fallback we want to log a warning & simply act as if the policy wasn't found by throwing the same contingency exception as the API does
LOGGER.warn("Falling back to circuit-breaker for getting policy {}", policyId);
throw new PolicyNotFoundException(policyId);
}
}

This is the default behavior of hystrix. "If command has a fallback then only first exception that trigers fallback logic will be propagated to caller"
See the error propagation section here.

I do this:
#Component
public class HystrixClient {
#HystrixCommand(ignoreExceptions = {ClientArgumentException.class})
public POJO getPojo(String id)
throws ClientNoDataFoundException, ClientArgumentException, ClientGeneralException {
//call my service and return POJO
}
}
#Component
public TrueClientUsedForAnotherSerivce {
#Autowired
HystrixClient hystrixClient;
public POJO getPojo(String id)
throws ClientNoDataFoundException, ClientArgumentException, ClientGeneralException, ClientOpenCircuitException {
try {
POJO result = hystrixClient.getCellular(id);
return result;
}
catch(HystrixRuntimeException e) {
LOG.debug("The circuit is open");
throw new ClientOpenCircuitException("Open circuit");
}
}
It only works if #HystrixCommand method is in another class.

Related

Different AuthenticationManager per path/route in spring security in MvC

Preamble
Since there are a lot of questions on StackOverflow about this already, I first want to ensure that this is not a duplicate and differentiate.
This is about
Having 2(or more) different AuthenticationProviders in 2 different AuthenticationManagers to be used on different routes.
Using the methods in Spring Security 5.5 not 3.x
Using a non XML configuration based approach
So the question is not about:
How to include several AuthenticationProvideres in on AuthenticationManager to offer "alternative authentications" (which most questions tend to be)
Case
Assume one has 2 custom AuthenticationProviders: CATApiTokenProvider and DOGApiTokenProvider. It is by design that we not talk about AOuth/JWT/Basic/Form providers, since they offer shortcuts.
Now we have 2 REST API endpoints /dog/endpoint and /cat/endpoint.
Question
How would one properly implement this today, with Spring Security 5.5:
We want the authentication provider CATApiTokenProvider to only be able to authenticate requests on /cat/endpoint
We want the authentication provider DOGApiTokenProvider to only be able to authenticate requests on /dog/endpoint
So one cannot authenticate with a cat token on /dog/endpoint and neither with a dog token on /cat/endpoint.
My Ideas/Approaches
a) I understand that since I have custom Cat/Dog filters, one can use the AuthenticationManagerResolver and pass one instance into the filter when creating the bean. This resolver might look like
public AuthenticationManagerResolver<HttpServletRequest> resolver()
{
return request -> {
if (request.getPathInfo().startsWith("/dog/")) {
try {
return ???;
} catch (Exception exception) {
log.error(exception);
}
}
if (request.getPathInfo().startsWith("/cat/")) {
try {
return ???;
} catch (Exception exception) {
log.error(exception);
}
}
};
}
Two questions with that would be:
how to return different authentication managers here? How to instantiate 2 different AM with each one CatAP and DogAP? Currently I use public void configure(AuthenticationManagerBuilder auth) but as far as I understand, I would only configure 'the one' AuthenticationManager and I could add DogAP and CatAP there, but this would let as having 1 AM with 2 APs, so when using this AM i could auth with the dog token on the cat endpoint
is this really the right way to implement this? I would have expected to be able to provide the AM on the SecurityConfiguration level
b) Somehow instantiate 2 different AuthenticationManagers and then use the SecurityConfiguration to assign them to different matchers.
Two questions:
what is the right way to spawn 2 different AMs with different providers?
I cannot understand how I would add an AM for a spec
http.authorizeRequests()
.antMatchers("/dog/**")
.?
You can either publish multiple filter chains or wire your own AuthenticationFilter with an AuthenticationManagerResolver
You may use AuthenticationManagerResolver to return different AuthenticationManagers. Since Spring Security 5.4.0, we don't need to extend the WebSecurityConfigurerAdapter to configure our SecurityFilterChain anymore, you can instead define a bean of SecurityFilterChain type.
I'll go into detail on wiring your own AuthenticationFilter.
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Bean
public SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated());
http.addFilterBefore(apiAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
private AuthenticationFilter apiAuthenticationFilter() {
AuthenticationFilter authenticationFilter = new AuthenticationFilter(new ApiAuthenticationManagerResolver(), new BasicAuthenticationConverter());
authenticationFilter.setSuccessHandler((request, response, authentication) -> {});
return authenticationFilter;
}
public static class ApiAuthenticationManagerResolver implements AuthenticationManagerResolver<HttpServletRequest> {
private final Map<RequestMatcher, AuthenticationManager> managers = Map.of(
new AntPathRequestMatcher("/dog/**"), new DogAuthenticationProvider()::authenticate,
new AntPathRequestMatcher("/cat/**"), new CatAuthenticationProvider()::authenticate
);
#Override
public AuthenticationManager resolve(HttpServletRequest request) {
for (Map.Entry<RequestMatcher, AuthenticationManager> entry : managers.entrySet()) {
if (entry.getKey().matches(request)) {
return entry.getValue();
}
}
throw new IllegalArgumentException("Unable to resolve AuthenticationManager");
}
}
public static class DogAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication.getName().endsWith("_dog")) {
return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(),
AuthorityUtils.createAuthorityList("ROLE_DOG"));
}
throw new BadCredentialsException("Username should end with _dog");
}
#Override
public boolean supports(Class<?> authentication) {
return true;
}
}
public static class CatAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication.getName().endsWith("_cat")) {
return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(),
AuthorityUtils.createAuthorityList("ROLE_CAT"));
}
throw new BadCredentialsException("Username should end with _cat");
}
#Override
public boolean supports(Class<?> authentication) {
return true;
}
}
}
In the example above, we have two AuthenticationProviders, one for cat and other for dog. They are resolved upon an AntPathRequestMatcher matching for both /dog/** and /cat/** endpoints, inside the ApiAuthenticationManagerResolver. There is no need to defined an AuthenticationManager for each dog and cat, since AuthenticationProvider/Manager have the same interface.
The ApiAuthenticationManagerResolver is then wired inside an AuthenticationFilter in your filter chain.
You can also define two different filter chains for each endpoint, like so:
#Bean
public SecurityFilterChain dogApiSecurity(HttpSecurity http) throws Exception {
http.requestMatchers((matchers) -> matchers
.antMatchers("/dog/**"));
http.authorizeRequests((authz) -> authz
.anyRequest().authenticated());
http.httpBasic();
http.authenticationProvider(new DogAuthenticationProvider());
return http.build();
}
#Bean
public SecurityFilterChain catApiSecurity(HttpSecurity http) throws Exception {
http.requestMatchers((matchers) -> matchers
.antMatchers("/cat/**"));
http.authorizeRequests((authz) -> authz
.anyRequest().authenticated());
http.httpBasic();
http.authenticationProvider(new CatAuthenticationProvider());
return http.build();
}
Please, when defining multiple filter chains, the ordering is important, make use of the #Order annotation in those scenarios.
When you do http.requestMatcher(new AntPathRequestMatcher("/endpoint/**")); you are telling Spring Security to only call the filter chain when the request matches that path.
There is also a ticket within Spring Security's repository to provide a AuthenticationManagerResolver implementation which accepts Map<RequestMatcher, AuthenticationManager>, it would be nice if you think it makes sense, give a thumbs up there.

Mock an Interface with Mockito return a NullPointerException

I m trying create unit tests for one project.I m facing a problem because when I try control the result of an interface(mock). When the code get the Interface variable that return a NullPointerException.
Firstly I tried #Override the method in my test class (ClassA), but it don't work. After that I tried mock the interface object and control the comportment with Mockito.When().tehnReturn();
I will put here my code, I read some solutions but none works.
My Interface:
#FunctionalInterface
public interface Interface {
UpdateXResponse process(UpdateXRequest request) throws Exception;
}
The class I want to test:
#Service(ClassA.class)
public class ClassA extends VService implements UpdateX {
#Reference
#Inject
private Interface interface;
#Inject
public ClassA(...) {...}
#Override
public UpdateXResponse process(UpdateXRequest request) throws Exception {
UpdateXResponse response = initResponse(context, request, new UpdateXResponse());
UpdateXInput input = request.getInput();
UpdateXOutput output = new UpdateXOutput();
response.setOutput(output);
try {
firstMethodCall(...);
} catch (Exception t) {
throwCorrectException(t, logger);
}
return response;
}
private void firstMethodCall(...) throws Exception {
TypeF typeF = callInterfaceMethod(...);
...
}
/**
* Orchestrates Interface service
*/
protected TypeF callInterfaceMethod(...) {
...
request.setInput(input);
request.setHeader(header);
InterfaceResponse response = interface.process(request); // LINE ERROR - In this step interface is NULL when the test get this
return response;
}
}
And finally my class test:
#RunWith(PowerMockRunner.class)
#PrepareForTest(value = {ClassA.class,Interface.class} )
public class WithPowerMockUnitTest{
#InjectMocks
private ClassA classA;
private Interface interface;
#Before
public void setUp() throws Exception {
InterfaceRequest InterfaceRequest = createInterfaceRequest();
InterfaceResponse serviceUnavailableResponse = createInterfaceResponse();
Interface = Mockito.mock(Interface.class);
when(Interface.process(Mockito.any(InterfaceRequest.class))).thenReturn(serviceUnavailableResponse);
}
#Test
public void testh() throws SOAException {
InterfaceResponse res = interface.process(Mockito.any(InterfaceRequest.class)); // There all run ok. The interface is not null and return what i expected.
System.out.println("RES "+res);
}
#Test
public void test() {
assertNotNull(classA); // not null
assertNotNull(interface); // not null
}
#Test
public void newTest() throws Exception {
InterfaceRequest InterfaceRequest = createInterfaceRequest();
InterfaceResponse serviceUnavailableResponse = createInterfaceResponse();
UpdateXResponse response = ClassA.process(updateXRequest()); // PROBLEM!! When that get the LINE ERROR the interface is null! WHY?
}
}
I put some comments in the lines where the problem exists for me.
public interface A{
Response process(Request r) throws Exception;
}
public class B{
private Class_That_Override_Interface_method ctoim;
public Response function(){
X res = method_B();
}
protected X method_B(){
response res = ctoim.process(request); // That ctoim is always NULL when the test get that line/call
}
}
Thanks
You're missing the #Mock annotation on your Interface variable.
Therefore the mock is not injected into your classA and the newTest() fails. (In this case remove Interface = Mockito.mock(Interface.class); from the setUp method).
Alternativly remove the #InjectMocks annotation and create your class under test manually passing your mock into the constructor.
For this specific case (assuming its a different case from the last question)
there doesn't seem to be a need to involve PowerMockito. So unless you left out some relevant parts you might as well just use the MockitoJUnitRunner.
Ps.:
Also remeber what I said last time about compilable examples?
interface is a keyword and can't be used for variables.
You should also aim to write variables identical all the times (not Interface and interface / classA and ClassA)
And in case you haven't read it yet check out the help section about minmal reproducible examples.
Edit:
I fogot to mention that the line interface.process(Mockito.any(InterfaceRequest.class)); in testh() is actually invalid syntax. You should use ArgumentMatchers only for parameters of mocked methods.
Also consider adding the MockitoAnnotations.initMocks(this); to your setUp method, when using the PowerMockRunner.

Gwt Logging into Client UI from Server-side

I have created GWT app, in which I have a Vertical Panel where I log the details.
Client side logging I'm doing using logger
sample code is:
public static VerticalPanel customLogArea = new VerticalPanel();
public static Logger rootLogger = Logger.getLogger("");
logerPanel.setTitle("Log");
scrollPanel.add(customLogArea);
logerPanel.add(scrollPanel);
if (LogConfiguration.loggingIsEnabled()) {
rootLogger.addHandler(new HasWidgetsLogHandler(customLogArea));
}
And I'm updating my vertical log panel using this code
rootLogger.log(Level.INFO,
"Already Present in Process Workspace\n");
But now my question is , I have to log server side details also into my vertical log panel.
My serverside GreetingServiceImpl code is:
public boolean createDirectory(String fileName)
throws IllegalArgumentException {
Boolean result = false;
try {
rootLogger.log(Level.INFO,
"I want to log this to my UI vertical log Panel");
system.out.println("log this to UI");
File dir = new File("D:/GenomeSamples/" + fileName);
if (!dir.exists()) {
result = dir.mkdir();
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
Now I want to log sysoutprt statements to my UI from here. How can I achieve this. Now using rootLogger.log(Level.INFO,
"I want to log this to my UI vertical log Panel"); code it is logging this to eclipse console . But how to log this to my UI in client side.
Please let me know If anything wrong in this question.
If I understood you right, you want to see your server log entries in web interface. And of course, java logger and printStackTrace() won't help you in that: your gwt code is compiled to JavaScript and has nothing to do with console and log files. Besides, your server can't "push" log entries to client - it's up to client to make requests. So if you want to track new log entries and move it to client, you need to poll server for new entries. And yet another problem: you may have many clients polling your servlet and you should keep in mind this multi-threading.
This is how I see probable implementation (it's just concept, may contain some errors and misspellings):
Remote interface:
public interface GreetingService extends RemoteService {
List<String> getLogEntries();
boolean createDirectory(String fileName)throws IllegalArgumentException;
}
Remote Servlet:
public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService {
public static final String LOG_ENTRIES = "LogEntries";
public List<String> getLogEntries() {
List<String> entries = getEntriesFromSession();
List<String>copy = new ArrayList<String>(entries.size());
copy.addAll(entries);
//prevent loading the same entries twice
entries.clear();
return copy;
}
public boolean createDirectory(String fileName)throws IllegalArgumentException {
Boolean result = false;
try {
log("I want to log this to my UI vertical log Panel");
log("log this to UI");
File dir = new File("D:/GenomeSamples/" + fileName);
if (!dir.exists()) {
result = dir.mkdir();
}
} catch (Exception e) {
log("Exception occurred: " + e.getMessage());
}
return result;
}
private List<String> getEntriesFromSession() {
HttpSession session= getThreadLocalRequest().getSession();
List<String>entries = (List<String>)session.getAttribute(LOG_ENTRIES);
if (entries == null) {
entries = new ArrayList<String>();
session.setAttribute(LOG_ENTRIES,entries);
}
return entries;
}
private void log(String message) {
getEntriesFromSession().add(message);
}
Simple implementation of polling (gwt client-side):
Timer t = new Timer() {
#Override
public void run() {
greetingAsyncService.getLogEntries(new AsyncCallBack<List<String>>() {
void onSuccess(List<String>entries) {
//put entries to your vertical panel
}
void onFailure(Throwable caught){
//handle exceptions
}
});
}
};
// Schedule the timer to run once in second.
t.scheduleRepeating(1000);
greetingAsyncService.createDirectory(fileName, new AsyncCallBack<Void>(){
void onSuccess(List<String>entries) {
//no need to poll anymore
t.cancel();
}
void onFailure(Throwable caught){
//handle exceptions
}
});
}
As you can see, I have used session to keep log entries, because session is client-specific and so different clients will receive different logs. It's up to you to decide what to use - you may create your own Logger class that will track users itself and give appropriate logs to appropriate clients.
And also you may want to save level of your messages (INFO,ERROR etc.) and then display messages in different colors (red for ERROR, for instance). To do so, you need to save not List, but some your custom class.
You'd create a logging servlet that has the same methods as your logging framework to send log messages to your server via RPC.
Here are some sample RPC log methods you can use:
public interface LogService extends RemoteService {
public void logException(String logger, String priority, String message, String error, StackTraceElement[] stackTrace, String nativeStack);
}
public interface LogServiceAsync {
public void logException(String logger, String priority, String message, String error, StackTraceElement[] stackTrace, String nativeStack, AsyncCallback<Void> callback);
}
public class LogServiceImpl extends RemoteServiceServlet implements LogService {
public void logException(String loggerName, String priority, String logMessage, String errorMessage, StackTraceElement[] stackTrace, String nativeStack) {
Logger logger = getLogger(loggerName);
Level level = getLevel(priority);
// Create a Throwable to log
Throwable caught = new Throwable();
if (errorMessage != null && stackTrace != null) {
caught = new Throwable(errorMessage);
caught.setStackTrace(stackTrace);
}
//do stuff with the other passed arguments (optional)
logger.log(level, message, caught);
}
}
Although those implementations are very nice, forget about timers and repeated server queries. We've something better now.
It's possible to push data from server to client using Atmosphere which supports WebSockets.

Lifestyle of Castle.Windsor interceptors

In my [Webmethod]s I have code like this.
var something = container.ResolveSomething();
something.Run();
All registered components except one have lifestyle defined as PerWebRequest. One is registered as Singleton (logger).
For some of components I have defined and configured Interceptor that will log method calls and their results.
My question is: Will I have problems if I register this Interceptor with Lifestyle PerWebRequest? Documentation advices to make all Interceptors Transient and use other lifestyles if we are really sure we want to do it. If I register Interceptors with lifestyle Transient any of my about 100 methods will have to look like this.
IComponent component = null;
try
{
component = container.ResolveComponent();
compoment.Run();
}
finally
{
container.Release(component);
}
So more boilerplate then real code.
Here is my interceptor:
public class LoggingInterceptor : IInterceptor
{
private readonly ILogger logger;
public LoggingInterceptor(ILogger logger)
{
this.logger = logger;
}
public void Intercept(IInvocation invocation)
{
var call = string.Format("{0}.{1}({2})", invocation.TargetType.FullName, invocation.Method.Name, string.Join(", ", invocation.Arguments.Select(arg => arg.ToString()).ToArray()));
try
{
logger.Info(call);
invocation.Proceed();
logger.Info("Result: " + call + " = " + invocation.ReturnValue);
}
catch (Exception e)
{
logger.Error(call, e);
throw;
}
}
}
I know WCF is better prepaired for IoC but I have to stay with ASP.NET WebServices.
My understanding is that even if the Interceptor is transient, it's lifetime is bound with intercepted component. It will be released with intercepted component because it is tracked by container.

Entity Framework - DbUpdateException to a custom Exception

I am using Entity Framework and when a DbUpdateException is thrown from dbContext.SaveChanges() how do I create a custom exception and throw that instead?
Would I create a condition on each SQL constraint that can be thrown:
if (e.InnerException.InnerException.Message.Contains("UNIQUE KEY"))
{
throw new CustomException("message");
}
EDIT: That approach makes good sense to me. If you know your application/DB is going to have a specific error, and it will help you or your users to have a specific custom exception type that quickly identifies what would otherwise be a somewhat complex or specific scenario, then absolutely yes. It's good practice to use both the exception type and the exception message to make the error as clear as possible. My code below is an even simpler example than what you seem to drilling down into. Rather than letting other code end up with a null reference exception or some other consequence, I beat everything to the punch with throw new DatabaseDataNotFoundException("Cannot find ServerAppConfiguration value for {0}", key);.
Just make your own exception class that inherits from Exception, here's a custom exception I use for exactly that purpose:
public class DatabaseDataNotFoundException : Exception
{
public DatabaseDataNotFoundException() : base() { }
public DatabaseDataNotFoundException(string message) : base(message) { }
public DatabaseDataNotFoundException(string message, params object[] args)
: base(String.Format(CultureInfo.CurrentCulture, message, args)) { }
public DatabaseDataNotFoundException(string message, Exception inner)
: base(message, inner) { }
public DatabaseDataNotFoundException(string message, Exception inner, params object[] args)
: base(String.Format(CultureInfo.CurrentCulture, message, args), inner) { }
protected DatabaseDataNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
Then your code becomes:
if (e.InnerException.InnerException.Message.Contains("UNIQUE KEY"))
{
throw new DatabaseDataNotFoundException("message");
}