I am working on a small project, I am trying to create a layer that will handle exception and logging.
This layer will sit in between User interface and DAL, mostly like BAL which will have some generic method that will then initiate further call to Data access layer.
Some thing like this
Public Class ExceptionHandler
{
//which should take a method name,parameters and return a object.
Public T InitiateDatabaseCall(//method name as input,parameters)
{
try
{
//then make the call to the method using the input parameter and pass the parameters
}
catch(Exception e)
{
// do logging
}
}
This layer will act as center repository to handle and log exception. I am not able to create the method that i described can experts provide some snippet that will show case this scenario.
Edited: With code added
static void Main(string[] args)
{
BAL b = new BAL();
var ll = b.GetFieldList("xxxxyyyy");
}
public class BAL
{
public List<Fields> GetFieldList(string screen)
{
if(!string.IsNullOrEmpty(screen))
{
ExceptionHandler.InitiateCall(() =>GetList(screen) ));
}
}
}
public static class ExceptionHandler
{
public T InitiateCall<T>(Func<T>method,object[] parms) where T : object
{
try
{
return method.Invoke();
}
catch(Exception ex)
{
return default(T);
}
}
}
public class DAL
{
public List<Fields> GetList(string name)
{
VipreDBDevEntities context = new VipreDBDevEntities();
return context.Database.SqlQuery<Fields>("SCREEN_FIELDS_SELECT #SCREEN_NAME", name).ToList();
}
}
It gives error GetList() does not exist in current context.
For these kind of things AOP (Aspect Oriented Programming, see https://en.wikipedia.org/wiki/Aspect-oriented_programming) is a really good fit.
These are cross-cutting concerns that are cluttering code if not done properly.
See for an example AOP framework PostSharp. Even with the free version that is simple to code. There are also (might be paid) build-in aspects for that, like http://doc.postsharp.net/exception-tracing.
A simple alternative is using a Func or Action (try it out in a Console App):
static void Main(string[] args)
{
ExceptionHandler.InitiateDatabaseCall(() => CallDb("Dummy"));
ExceptionHandler.InitiateDatabaseCall<int>(() => { throw new InvalidOperationException(); });
}
int CallDb(string justToShowExampleWithParameters)
{
return 5;
}
public static class ExceptionHandler
{
public static T InitiateDatabaseCall<T>(Func<T> method)
{
try
{
return method.Invoke();
}
catch (Exception e)
{
// do logging
Console.WriteLine(e.Message);
return default(T); // or `throw` to pass the exception to the caller
}
}
}
Edit:
based on your added code in the question you can solve the error about GetList() by some minor modifications:
static void Main(string[] args) {
BAL b = new BAL();
var ll = b.GetFieldList("xxxxyyyy");
}
public class BAL
{
public List<Fields> GetFieldList(string screen)
{
if (!string.IsNullOrEmpty(screen))
{
return ExceptionHandler.InitiateCall(() => new DAL().GetList(screen)); // Slight modification of your code here
}
else
{
return null; // or whatever fits your needs
}
}
}
public class ExceptionHandler
{
public static T InitiateCall<T>(Func<T> method)
{
try
{
return method.Invoke();
}
catch (Exception ex)
{
//log
return default(T);
}
}
}
public class DAL
{
public List<Fields> GetList(string name)
{
VipreDBDevEntities context = new VipreDBDevEntities();
return context.Database.SqlQuery<Fields>("SCREEN_FIELDS_SELECT #SCREEN_NAME", name).ToList();
}
}
You don't need the object[] parms parameter in InitiateCall given the provided code. Any paramaters you need for the method call are given in the Func<T>
Personally, I think loging should be done in two ways:
Step logging (when you log some step in your code)
Scope logging (when you log begin/end of some code or it's time)
So, I always choose to create ILogger class with these two ways:
public sealed class Logger : ILogger
{
private readonly Serilog.ILogger _seriLogger;
public Logger(Serilog.ILogger seriLogger)
{
_seriLogger = seriLogger;
}
public void Debug(string format, params object[] args)
{
_seriLogger.Debug(format, args);
}
public void Info(string format, params object[] args)
{
_seriLogger.Information(format, args);
}
public void Warn(string format, params object[] args)
{
_seriLogger.Warning(format, args);
}
public void Error(Exception e, string format, params object[] args)
{
_seriLogger.Error(e, format, args);
}
public void Fatal(Exception e, string format, params object[] args)
{
_seriLogger.Fatal(e, format, args);
}
public IDisposable GetScope(string name, long timeout = 0)
{
return new LoggerScope(this, name, timeout);
}
}
internal class LoggerScope : IDisposable
{
private readonly ILogger _logger;
private readonly string _name;
private readonly long _timeout;
private readonly Stopwatch _sw;
private bool ExceedScope
{
get { return _timeout > 0; }
}
public LoggerScope(ILogger logger, string name, long timeout)
{
_logger = logger;
_name = name;
_timeout = timeout;
if (!ExceedScope)
{
_logger.Debug("Start execution of {0}.", name);
}
_sw = Stopwatch.StartNew();
}
public void Dispose()
{
_sw.Stop();
if (ExceedScope)
{
if (_sw.ElapsedMilliseconds >= (long)_timeout)
{
_logger.Debug("Exceeded execution of {0}. Expected: {1}ms; Actual: {2}ms.", _name, _timeout.ToString("N"), _sw.Elapsed.TotalMilliseconds.ToString("N"));
}
}
else
{
_logger.Debug("Finish execution of {0}. Elapsed: {1}ms", _name, _sw.Elapsed.TotalMilliseconds.ToString("N"));
}
}
}
Then, If I want to log something, I use it like this, without AOP:
using(_log.GetScope("Some describable name"))
{
//Some code here
}
Related
I have a public method ValidateWords inside FooService.To test the ValidateWord method, I created IAppSettingWrapper and AppSettingWrapper which returns the Instance of AppSettings.
Inside the test method, I want to substitute NotAllowedWords using NSubstitute. However, it throws an object reference exception. Is there any way for substitution? If it's not possible, how can I refactor my static instance?
public sealed class AppSettings
{
private static object _lockObject = new object();
private static volatile AppSettings? _instance;
private static DateTime _cacheTime;
private Settings[] _settings;
public AppSettings()
{
try
{
_settings = GetSettings();
}
catch { }
}
public static AppSettings Instance
{
get
{
lock (_lockObject)
{
if (_instance == null)
{
_instance = new AppSettings();
}
}
return _instance;
}
}
public List<string> NotAllowedWords
{
get
{
return new List<string>() {
"index",
"change"
};
}
}
public T GetValues<T>(string key,T defaultValue)
{
T result = defaultValue;
var settings = _settings.Where(i => i.Key == key).FirstOrDefault();
result = (T)Convert.ChangeType(settings.Value, typeof(T));
return result;
}
private Settings[]? GetSettings()
{
//gets data from web services
return base.Channel.GetSettings();
}
}
public class Settings
{
public string Key { get; set; }
public string Value { get; set; }
}
public interface IAppSettingsWrapper
{
public AppSettings Instance();
}
public class AppSettingsWrapper : IAppSettingsWrapper
{
public AppSettings Instance()
{
return AppSettings.Instance;
}
}
[TestClass]
public class FooServiceTest{
private IAppSettingsWrapper _appSettingsWrapper;
[TestInitialize]
public void TestInitialize(IAppSettingsWrapper appSettingsWrapper)
{
_appSettingsWrapper = Substitute.For<IAppSettingsWrapper>();
}
private FooService CreateFooService()
{
return new FooService(_appSettingsWrapper);
}
[TestMethod]
public void Throw_Exception_When_Given_Word_Not_Allowed() {
var service = this.CreateFooService();
_appSettingsWrapper.Instance().NotAllowedWords.Returns(new List<string> { "index" });
var word = "index";
Exception ex = Assert.ThrowsException<Exception>(() => service.ValidateWords(word));
Assert.AreEqual("this word is not allowed", ex.Message);
}
}
public class FooService
{
private IAppSettingsWrapper _appSettingsWrapper;
public FooService(IAppSettingsWrapper appSettingsWrapper)
{
_appSettingsWrapper = appSettingsWrapper;
}
public void ValidateWords(string word)
{
if (_appSettingsWrapper.Instance().NotAllowedWords.Contains(word))
{
throw new Exception("this word is not allowed");
}
}
}
The AppSettings.NotAllowedWords property is not substitutable due to it not being virtual and the class being sealed. If you add NSubstitute.Analyzers to your test project it will help you find these cases. (The How NSubstitute Works documentation outlines why this is the case.)
One option is to make AppSettings implement an IAppSettings interface and inject that into FooService (rather than the wrapper). Then you can use a substitute for tests, and AppSettings.Instance for your real code.
In a WinForms app, there is Logger class that is a form designed for logging, so that any class can call it.
There is a static Configuration class, inside which a Logger lives.
Previous implementation
Various classes would call the logger like so:
public class ImportController
{
public void import()
{
try
{
// do the work...
}
catch (Exception ex)
{
Configuration.logger.log("Something failed");
Configuration.logger.log(ex);
}
}
}
Current implementation
The logger implements the following interface, which was extracted from it as part of refactoring to enable unit testing calling classes through dependency injection:
public interface ILogger
{
void (string message, [CallerMemberName] string member = "", [CallerLineNumberAttribute] int lineNumber = -1, string fileName = "");
void (Exception ex, [CallerMemberName] string member = "", [CallerLineNumberAttribute] int lineNumber = -1, string fileName = "");
}
As can be seen, the idea is to have it automatically log the calling class name and source file path.
The following is an example of an attempt to inject a logger into all classes that use it, in this instance the ImportController from above:
public class ImportControllerLogger
{
public void log(string message, [CallerMemberName] string member = "", [CallerLineNumber] int line_num = -1, string filename = "")
{
Configuration.log.log(string message, "ImportController", lineNumber, #"Controllers\ImportController.cs");
}
public void log(Exception exception, [CallerMemberName] string member = "", [CallerLineNumber] int line_num = -1, string filename = "")
{
Configuration.log.log(exception, "ImportController", lineNumber, #"Controllers\ImportController.cs");
}
}
public class ImportController
{
ILogger _logger;
public ImportController(ILogger logger)
{
this._logger = logger;
}
public void import()
{
try
{
// do the work...
}
catch (Exception ex)
{
_logger.log("Something failed");
_logger.log(ex);
}
}
}
Questions
Is this the correct approach to decouple the logger from all classes that use it?
It seems it might be better to create a single "LoggerHelper" class, that abstracts away the logger so that any class can make a call to it, instead of creating such a class for every calling class. How can the name of the calling class and source file path for the calling class be logged, in a proper way, without resorting to manually specifying it for each class? It worked in the previous implementation with the attributes.
I also had to implement something like that.
The code is simplified.
ILogger
public interface ILogger
{
event EventHandler<LogEventArgs> OnLogAdded;
Type Type { get; }
void Log(string message);
}
Logger
public class Logger : ILogger
{
public Type Type { get; }
public Logger(Type type)
{
Type = type;
}
public event EventHandler<LogEventArgs> OnLogAdded;
public void Log(string message)
{
EventHandler<LogEventArgs> handler = OnLogAdded;
handler?.Invoke(this, new LogEventArgs(message));
}
}
LogProvider
public static class LogProvider
{
private static List<ILogger> loggers = new List<ILogger>();
public static ILogger CreateLogger<T>()
{
if (loggers.Select(x => x.Type.Equals(typeof(T))).Count() > 0)
{
throw new Exception($"There is allready a logger for the type {typeof(T)}");
}
ILogger logger = new Logger(typeof(T));
logger.OnLogAdded += OnLogAdded;
loggers.Add(logger);
return logger;
}
private static void OnLogAdded(object sender, LogEventArgs e)
{
//add log to your config
}
}
And you can use it like this:
public class SampleView
{
private ILogger logger = LogProvider.CreateLogger<SampleView>();
public SampleView()
{
logger.Log("TestLog");
}
}
I don't know if this is the best implementation, but it works like a charm.
I currently wrote an Interceptor which code is below
public class TransactionalInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
using (var transaction = ...)
{
try
{
invocation.Proceed();
transaction.Commit();
}
catch
{
transaction.Rollback();
}
finally
{
transaction.Dispose();
}
}
}
}
but when register this interceptor it will apply to all methods. I have a service class with a repository injected having CRUD methods.
I don't want a transaction to be opened for query methods.
I read this link but I cannot figure out how to apply it to my code
http://docs.autofac.org/en/latest/advanced/adapters-decorators.html#decorators
I don't know who to refactor my TransactionalInterceptor (and register it) to use it in a class like this code
[Intercept(typeof(LoggerInterceptor))] //logger
public class SomeService : ISomeService
{
private readonly ISomeRepository someRepository;
public SomeService(SomeRepository someRepository)
{
this.someRepository = someRepository;
}
public IEnumerable<SomeDto> GetAll()
{
// code
}
public SomeDto GetById()
{
// code
}
[Transactional]
public int Create(SomeDto someDto)
{
// code to insert
}
}
The invocation parameter of the Intercept method contains a Method property which is a MethodInfo of the method currently intercepted.
You can use this property to do what you want.
For example by using the method name :
public void Intercept(IInvocation invocation)
{
if (invocation.MethodInvocationTarget.Name != nameof(ISomeService.Create))
{
invocation.Proceed();
return;
}
using (var transaction = ...)
{
try
{
invocation.Proceed();
transaction.Commit();
}
catch
{
transaction.Rollback();
}
finally
{
transaction.Dispose();
}
}
}
or based on an attribute from the target method :
if (!invocation.MethodInvocationTarget
.CustomAttributes
.Any(a => a.AttributeType == typeof(TransactionalAttribute)))
You can also use the IInterceptorSelector type but it requires more work to register it with Autofac
I solved the problem with ProxyGenerationHook. See the answer
Create your custom attribute for selecting which method to intercept. This attribute's target should be Method.
[System.AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
sealed class UseInterceptorAttribute : Attribute
{
public UseInterceptorAttribute()
{
}
}
Create your service interface and service class:
public interface ISomeService
{
void GetWithoutInterceptor();
[UseInterceptor]
void GetWithInterceptor();
}
public class SomeService
{
void GetWithoutInterceptor()
{
//This method will not be intercepted...
}
[UseInterceptor]
void GetWithInterceptor()
{
//This method will be intercepted...
}
}
Create your ProxyGenerationHook
public class SomeServiceProxyGenerationHook : IProxyGenerationHook
{
public void MethodsInspected()
{
}
public void NonProxyableMemberNotification(Type type, MemberInfo memberInfo)
{
}
public bool ShouldInterceptMethod(Type type, MethodInfo methodInfo)
{
return methodInfo
.CustomAttributes
.Any(a => a.AttributeType == typeof(UseInterceptorAttribute));
}
}
Don't use attributes for enabling interceptors. Enable it when
registering your service like this:
public class AutofacDependencyResolver
{
private readonly IContainer _container;
public AutofacDependencyResolver()
{
_container = BuildContainer();
}
private IContainer BuildContainer()
{
var proxyGenerationOptions = new ProxyGenerationOptions(new ProductServiceProxyGenerationHook());
builder.RegisterType<SomeService>()
.As<ISomeService>()
.EnableInterfaceInterceptors(proxyGenerationOptions)
.InterceptedBy(typeof(TransactionalInterceptor))
builder.Register(c => new TransactionalInterceptor());
return builder.Build();
}
public T GetService<T>()
where T:class
{
var result = _container.TryResolve(out T serviceInstance);
return serviceInstance ?? throw new Exception($"The service could not found: {nameof(T)}");
}
}
This solution is following this article
Also I uploaded the minimal example about this solution.
also can try, it is simple https://fs7744.github.io/Norns.Urd/index.html
public class AddTenInterceptorAttribute : AbstractInterceptorAttribute
{
public override void Invoke(AspectContext context, AspectDelegate next)
{
next(context);
AddTen(context);
}
private static void AddTen(AspectContext context)
{
if (context.ReturnValue is int i)
{
context.ReturnValue = i + 10;
}
else if(context.ReturnValue is double d)
{
context.ReturnValue = d + 10.0;
}
}
public override async Task InvokeAsync(AspectContext context, AsyncAspectDelegate next)
{
await next(context);
AddTen(context);
}
}
[AddTenInterceptor]
public interface IGenericTest<T, R> : IDisposable
{
// or
//[AddTenInterceptor]
T GetT();
}
Is there in C# some kind of equivalent of ExpectedSystemExit in Java? I have an exit in my code and would really like to be able to test it. The only thing I found in C# is a not really nice workaround.
Example Code
public void CheckRights()
{
if(!service.UserHasRights())
{
Environment.Exit(1);
}
}
Test Code
[TestMethod]
public void TestCheckRightsWithoutRights()
{
MyService service = ...
service.UserHasRights().Returns(false);
???
}
I am using the VS framework for testing (+ NSubstitute for mocking) but it is not a problem to switch to nunit or whatever for this test.
You should use dependency injection to supply to the class being tested an interface that provides an environmental exit.
For example:
public interface IEnvironment
{
void Exit(int code);
}
Let's also assume that you have an interface for calling UserHasRights():
public interface IRightsService
{
bool UserHasRights();
}
Now suppose your class to be tested looks like this:
public sealed class RightsChecker
{
readonly IRightsService service;
readonly IEnvironment environment;
public RightsChecker(IRightsService service, IEnvironment environment)
{
this.service = service;
this.environment = environment;
}
public void CheckRights()
{
if (!service.UserHasRights())
{
environment.Exit(1);
}
}
}
Now you can use a mocking framework to check that IEnvironment .Exit() is called under the right conditions. For example, using Moq it might look a bit like this:
[TestMethod]
public static void CheckRights_exits_program_when_user_has_no_rights()
{
var rightsService = new Mock<IRightsService>();
rightsService.Setup(foo => foo.UserHasRights()).Returns(false);
var enviromnent = new Mock<IEnvironment>();
var rightsChecker = new RightsChecker(rightsService.Object, enviromnent.Object);
rightsChecker.CheckRights();
enviromnent.Verify(foo => foo.Exit(1));
}
Ambient contexts and cross-cutting concerns
A method such as Environment.Exit() could be considered to be a cross-cutting concern, and you might well want to avoid passing around an interface for it because you can end up with an explosion of additional constructor parameters. (Note: The canonical example of a cross cutting concern is DateTime.Now.)
To address this issue, you can introduce an "Ambient context" - a pattern which allows you to use a static method while still retaining the ability to unit test calls to it. Of course, such things should be used sparingly and only for true cross-cutting concerns.
For example, you could introduce an ambient context for Environment like so:
public abstract class EnvironmentControl
{
public static EnvironmentControl Current
{
get
{
return _current;
}
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
_current = value;
}
}
public abstract void Exit(int value);
public static void ResetToDefault()
{
_current = DefaultEnvironmentControl.Instance;
}
static EnvironmentControl _current = DefaultEnvironmentControl.Instance;
}
public class DefaultEnvironmentControl : EnvironmentControl
{
public override void Exit(int value)
{
Environment.Exit(value);
}
public static DefaultEnvironmentControl Instance => _instance.Value;
static readonly Lazy<DefaultEnvironmentControl> _instance = new Lazy<DefaultEnvironmentControl>(() => new DefaultEnvironmentControl());
}
Normal code just calls EnvironmentControl.Current.Exit(). With this change, the IEnvironment parameter disappears from the RightsChecker class:
public sealed class RightsChecker
{
readonly IRightsService service;
public RightsChecker(IRightsService service)
{
this.service = service;
}
public void CheckRights()
{
if (!service.UserHasRights())
{
EnvironmentControl.Current.Exit(1);
}
}
}
But we still retain the ability to unit-test that it has been called:
public static void CheckRights_exits_program_when_user_has_no_rights()
{
var rightsService = new Mock<IRightsService>();
rightsService.Setup(foo => foo.UserHasRights()).Returns(false);
var enviromnent = new Mock<EnvironmentControl>();
EnvironmentControl.Current = enviromnent.Object;
try
{
var rightsChecker = new RightsChecker(rightsService.Object);
rightsChecker.CheckRights();
enviromnent.Verify(foo => foo.Exit(1));
}
finally
{
EnvironmentControl.ResetToDefault();
}
}
For more information about ambient contexts, see here.
I ended up creating a new method which I can then mock in my tests.
Code
public void CheckRights()
{
if(!service.UserHasRights())
{
Environment.Exit(1);
}
}
internal virtual void Exit()
{
Environment.Exit(1);
}
Unit test
[TestMethod]
public void TestCheckRightsWithoutRights()
{
MyService service = ...
service.When(svc => svc.Exit()).DoNotCallBase();
...
service.CheckRights();
service.Received(1).Exit();
}
If your goal is to avoid extra classes/interfaces just to support tests, how do you feel about Environment.Exit action via Property Injection?
class RightsChecker
{
public Action AccessDeniedAction { get; set; }
public RightsChecker(...)
{
...
AccessDeniedAction = () => Environment.Exit();
}
}
[Test]
public TestCheckRightsWithoutRights()
{
...
bool wasAccessDeniedActionExecuted = false;
rightsChecker.AccessDeniedAction = () => { wasAccessDeniedActionExecuted = true; }
...
Assert.That(wasAccessDeniedActionExecuted , Is.True);
}
I'm using the heavily-undocumented Castle dynamic-proxy system. I've managed to make it do almost everything I want, except for one thing: How do you make a proxied method throw an exception instead of returning a value?
public sealed class MyInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
if (CheckArgs(invocation.Arguments))
{
invocation.ReturnValue = DoRealWork(invocation.Arguments);
}
else
{
invocation.Exception = new InvalidOperationException(); // How?
}
}
}
From the point of view of the proxied object the interceptor is not visible; it simply calls its own virtual method, and DynamicProxy invokes the correct interceptor methods before returning the ReturnValue to the caller.
So if you want to throw an exception just throw it from the interceptor:
if (CheckArgs(invocation.Arguments))
{
invocation.ReturnValue = DoRealWork(invocation.Arguments);
}
else
{
throw new InvalidOperationException();
}
From the point of view of the caller it will be an exception in the called method.
Edit for comment:
Regarding the type of the exception thrown in the generator I have the correct type, not a wrapper:
public interface IDummy
{
string DoSomething();
}
public class Dummy: IDummy {
public virtual string DoSomething()
{
return string.Empty;
}
}
public class MyCustomException : Exception {}
public class CustomIntercept: IInterceptor
{
public void Intercept(IInvocation invocation)
{
throw new MyCustomException();
}
}
internal class Program
{
private static void Main(string[] args)
{
var pg = new ProxyGenerator();
GetValue(pg.CreateInterfaceProxyWithoutTarget<IDummy>(new CustomIntercept()));
GetValue(pg.CreateClassProxy<Dummy>(new CustomIntercept()));
GetValue(pg.CreateClassProxyWithTarget<Dummy>(new Dummy(), new CustomIntercept()));
GetValue(pg.CreateInterfaceProxyWithTarget<IDummy>(new Dummy(), new CustomIntercept()));
}
private static void GetValue(IDummy dummy)
{
try
{
dummy.DoSomething();
}
catch (Exception e)
{
Console.WriteLine(e.GetType().Name);
}
}
}
All four outputs are MyCustomException
Can you make sure that the TargetInvocationException doesn't come from your own code? What version of the DynamicProxy are you using (I'm using the one in Castle.Core 3.2)