DI container giving new instance every time? - c#

It seems my DI container makes a new instance for ChromeDriver (IWebDriver) each time I try and get it from the container? All of this happened after refactoring my code. I suddenly needed Selenium by reference for the methods below, otherwise, it wouldn't update the DOM throughout new page loads as I was passing it by value.
Here are the original methods before refactoring,
public static bool ElementExists(IWebDriver selenium, By selector)
{
try
{
selenium.FindElement(selector);
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
public static void WaitForElements(IWebDriver selenium, List<By> selectors, string name = "")
{
new ConsoleLogger().Trace("Waiting for " + (string.IsNullOrEmpty(name) ? selectors.Count + " items" : name) + ", give us a second.");
while (selectors.Where(x => ElementExists(selenium, x)).Count() < selectors.Count)
{
Thread.Sleep(100);
}
}
I thought hmm, this is going to be a tricky one. I needed some sort of static instance that I could always pass by reference, I refactored it to this.
public static bool ElementExists(By selector)
{
var selenium = Reusables.GetServiceProvider().GetService<IWebDriver>();
try
{
selenium.FindElement(selector);
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
Reusables class:
public static class Reusables
{
public static IDependencyProvider DependencyProvider;
public static IServiceProvider GetServiceProvider()
{
return DependencyProvider.BuildServiceProvider();
}
}
Program:
private static void Main(string[] args)
{
var diProvider = new DependencyProvider();
Reusables.DependencyProvider = diProvider;
Console.ForegroundColor = ConsoleColor.White;
Console.CancelKeyPress += (sender, eArgs) => {
QuitEvent.Set();
eArgs.Cancel = true;
};
Console.WriteLine();
Console.CursorVisible = false;
/*var config = serviceProvider.GetService<IConfigProvider>();
config.Load("https://kskdkskd.kdskdkk", new WebClient());*/
var scraper = Reusables.GetServiceProvider().GetService<IScraperHandler>();
scraper.Start();
QuitEvent.WaitOne();
}
Not sure if its needed, but here's how I register my dependencies:
public class DependencyProvider : ServiceCollection, IDependencyProvider
{
public DependencyProvider()
{
Register();
}
public void Register()
{
this.AddSingleton<IAuthProvider, AuthProvider>();
var options = new ChromeOptions();
options.AddArguments("--disable-notifications");
options.SetLoggingPreference(LogType.Browser, LogLevel.Off);
this.AddSingleton<IWebDriver>(provider =>
new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), options)
);
var links = File.ReadAllLines("***");
this.AddSingleton<IHttpHandler, HttpHandler>();
this.AddSingleton<IEnumerable>(stack => new Stack<string>(links.ToList()));
this.AddSingleton<IScraperHandler, ScraperHandler>();
this.AddSingleton<IConfigProvider, JsonConfigProvider>();
}
}

You are building a new service provider every time Reusables.GetServiceProvider is invoked. Which will result in a new provider each time, resulting in a new instance every time the new service provider gets a service.
If the goal is to have a single provider then a singleton is required.
public static class Reusables {
public static IDependencyProvider DependencyProvider;
private static Lazy<IServiceProvider> serviceProvider =
new Lazy<IServiceProvider>(() => DependencyProvider.BuildServiceProvider());
public static IServiceProvider GetServiceProvider() {
return serviceProvider.Value;
}
}
Not a big fan of the above design but it should work.

Related

Lucene.net - singleton + queue = write.lock issue

I have a strange problem. Here's the scenario:
I use Lucene.net, and I have the IndexWriter object as a singleton, because of the write.lock file. Also, only one thread is accessing to that object thanks to the RabbitMQ.
But I still get the error System.IO.IOException: The process can not access the file "e:\MyIndex\write.lock" because it is being used by another process.
I thought it can be caused by IIS pool recycle, as mentioned here https://medium.com/#ShamreshKhan/turning-off-iis-app-pool-overlap-recycle-6d2591faa312#.qnflhui4z but it didn't fix the problem. What else do I have to check what can cause the problem ?
My IndexWirter singleton:
public class Singletons
{
private Singletons()
{
if (directoryMyTemp == null)
directoryMyTemp = FSDirectory.Open(new DirectoryInfo(luceneMyDir));
if (IndexWriter.IsLocked(directoryMyTemp))
IndexWriter.Unlock(directoryMyTemp);
//just to be sure
lockFilePath = Path.Combine(luceneMyDir, IndexWriter.WRITE_LOCK_NAME);
if (File.Exists(lockFilePath))
File.Delete(lockFilePath);
}
private static readonly object singLock = new object();
private static readonly Lazy<Singletons> lazy = new Lazy<Singletons>(() => new Singletons());
private static readonly Lazy<IndexWriter> MyWriterLazy = new Lazy<IndexWriter>(() => new IndexWriter(MainInstance.MyDirectory, Workers.GetAnalyzer(), IndexWriter.MaxFieldLength.UNLIMITED));
public static IndexWriter MyInstance
{
get
{
return MyWriterLazy.Value;
}
}
public static Singletons MainInstance
{
get
{
return lazy.Value;
}
}
private static FSDirectory directoryMyTemp;
public FSDirectory MyDirectory
{
get
{
return directoryMyTemp;
}
}
}
index rebuild:
Singletons.MyInstance.DeleteAll();
for (int i = 0; i < count; i++)
{
try
{
//document update
}
catch (Exception)
{
Singletons.MyInstance.Rollback();
throw;
}
}
Singletons.MyInstance.Commit();

Ripple Effect: OutOfMemoryException

I have been trying to learn about Roslyn and see if it works for my needs.
In a very simple project I am trying to create a simple ‘Ripple Effect’, which is for each iteration causing a new assembly to be loaded and eventually after 500 iterations it crashes (OutOfMemoryException)
Is there a way to do this without causing it to explode?
class Program
{
static void Main(string[] args)
{
string code = #"
IEnumerable<double> combined = A.Concat(B);
return combined.Average();
";
Globals<double> globals = new Globals<double>()
{
A = new double[] { 1, 2, 3, 4, 5 },
B = new double[] { 1, 2, 3, 4, 5 },
};
ScriptOptions options = ScriptOptions.Default;
Assembly systemCore = typeof(Enumerable).Assembly;
options = options.AddReferences(systemCore);
options = options.AddImports("System");
options = options.AddImports("System.Collections.Generic");
options = options.AddImports("System.Linq");
var ra = CSharpScript.RunAsync(code, options, globals).Result;
for (int i = 0; i < 1000; i++)
{
ra = ra.ContinueWithAsync(code).Result;
}
}
}
public class Globals<T>
{
public IEnumerable<T> A;
public IEnumerable<T> B;
}
Exception Image
Everytime you use CSharpScript.Run or Evaluate method you are actually loading a new script (a .dll) which happens to be quite large. In order to avoid this you need te cache the script that you are executing by doing so:
_script = CSharpScript.Create<TR>(code, opts, typeof(Globals<T>)); // Other options may be needed here
Having _script cached you can now execute it by:
_script.RunAsync(new Globals<T> {A = a, B = b}); // The script will compile here in the first execution
If you have a few scripts to load with your application each time, this is the easiest thing to do. However a better solution is to use a separate AppDomain and load the script isolated. Here is one way of doing it:
Create a script executor proxy as MarshalByRefObject:
public class ScriptExecutor<TP, TR> : CrossAppDomainObject, IScriptExecutor<TP, TR>
{
private readonly Script<TR> _script;
private int _currentClients;
public DateTime TimeStamp { get; }
public int CurrentClients => _currentClients;
public string Script => _script.Code;
public ScriptExecutor(string script, DateTime? timestamp = null, bool eagerCompile = false)
{
if (string.IsNullOrWhiteSpace(script))
throw new ArgumentNullException(nameof(script));
var opts = ScriptOptions.Default.AddImports("System");
_script = CSharpScript.Create<TR>(script, opts, typeof(Host<TP>)); // Other options may be needed here
if (eagerCompile)
{
var diags = _script.Compile();
Diagnostic firstError;
if ((firstError = diags.FirstOrDefault(d => d.Severity == DiagnosticSeverity.Error)) != null)
{
throw new ArgumentException($"Provided script can't compile: {firstError.GetMessage()}");
}
}
if (timestamp == null)
timestamp = DateTime.UtcNow;
TimeStamp = timestamp.Value;
}
public void Execute(TP parameters, RemoteCompletionSource<TR> completionSource)
{
Interlocked.Increment(ref _currentClients);
_script.RunAsync(new Host<TP> {Args = parameters}).ContinueWith(t =>
{
if (t.IsFaulted && t.Exception != null)
{
completionSource.SetException(t.Exception.InnerExceptions.ToArray());
Interlocked.Decrement(ref _currentClients);
}
else if (t.IsCanceled)
{
completionSource.SetCanceled();
Interlocked.Decrement(ref _currentClients);
}
else
{
completionSource.SetResult(t.Result.ReturnValue);
Interlocked.Decrement(ref _currentClients);
}
});
}
}
public class Host<T>
{
public T Args { get; set; }
}
Create a proxy object to share data between script execution app domain and the main domain:
public class RemoteCompletionSource<T> : CrossAppDomainObject
{
private readonly TaskCompletionSource<T> _tcs = new TaskCompletionSource<T>();
public void SetResult(T result) { _tcs.SetResult(result); }
public void SetException(Exception[] exception) { _tcs.SetException(exception); }
public void SetCanceled() { _tcs.SetCanceled(); }
public Task<T> Task => _tcs.Task;
}
Create this helper abstract type that all the other remote ones need to inherit from:
public abstract class CrossAppDomainObject : MarshalByRefObject, IDisposable
{
private bool _disposed;
/// <summary>
/// Gets an enumeration of nested <see cref="MarshalByRefObject"/> objects.
/// </summary>
protected virtual IEnumerable<MarshalByRefObject> NestedMarshalByRefObjects
{
get { yield break; }
}
~CrossAppDomainObject()
{
Dispose(false);
}
/// <summary>
/// Disconnects the remoting channel(s) of this object and all nested objects.
/// </summary>
private void Disconnect()
{
RemotingServices.Disconnect(this);
foreach (var tmp in NestedMarshalByRefObjects)
RemotingServices.Disconnect(tmp);
}
public sealed override object InitializeLifetimeService()
{
//
// Returning null designates an infinite non-expiring lease.
// We must therefore ensure that RemotingServices.Disconnect() is called when
// it's no longer needed otherwise there will be a memory leak.
//
return null;
}
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
Disconnect();
_disposed = true;
}
}
Here is how we use it:
public static IScriptExecutor<T, R> CreateExecutor<T, R>(AppDomain appDomain, string script)
{
var t = typeof(ScriptExecutor<T, R>);
var executor = (ScriptExecutor<T, R>)appDomain.CreateInstanceAndUnwrap(t.Assembly.FullName, t.FullName, false, BindingFlags.CreateInstance, null,
new object[] {script, null, true}, CultureInfo.CurrentCulture, null);
return executor;
}
public static AppDomain CreateSandbox()
{
var setup = new AppDomainSetup { ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase };
var appDomain = AppDomain.CreateDomain("Sandbox", null, setup, AppDomain.CurrentDomain.PermissionSet);
return appDomain;
}
string script = #"int Square(int number) {
return number*number;
}
Square(Args)";
var domain = CreateSandbox();
var executor = CreateExecutor<int, int>(domain, script);
using (var src = new RemoteCompletionSource<int>())
{
executor.Execute(5, src);
Console.WriteLine($"{src.Task.Result}");
}
Note the usage of RemoteCompletionSource within a using block. If you forget to dispose it you will have memory leaks because instances of this object on the other domain (not the caller) will never get GCed.
Disclaimer: I took the idea of RemoteCompletionSource from
here, also the idea for the CrossAppDomainObject from public domain.

How to use only single NLog instance in whole DLL

I want to use NLog in DLL this is my code
namespace TestLogging
{
public class Class2
{
public void foo()
{
LogWrapper.initlog();
LogWrapper.write("Error message");
}
}
public static class LogWrapper
{
private static bool isInit = false;
private static Logger _logger = null;
public static void initlog()
{
// Make sure logger is initialized only once
if (!isInit)
{
var config = new LoggingConfiguration();
var fileTarget = new FileTarget();
config.AddTarget("file", fileTarget);
// Step 3. Set target properties
fileTarget.Layout = #"${date:format=HH\:mm\:ss} - ${message}";
fileTarget.FileName = "c:/myFolder/" + "${date:format=yyyy-MM-dd}.log";
var rule2 = new LoggingRule("*", LogLevel.Debug, fileTarget);
config.LoggingRules.Add(rule2);
// Step 5. Activate the configuration
LogManager.Configuration = config;
// Example usage
_logger = LogManager.GetLogger("Example");
isInit = true;
}
}
public static void write(string s)
{
if (isInit == false)
throw new Exception("Not initialized");
_logger.Error(s);
}
}
}
My goal is that many different classes in DLL should be able to use the NLog via this log wrapper. Configuration should be done once. Is my approach correctly implemented? Is it thread safe?
declare a static object at the class level
private static object _locker = new object();
in the initlog put a lock on it
lock(_locker)
{
//your code
}
in your write method do the same
lock(_locker)
{
if (isInit == false)
throw new Exception("Not initialized");
_logger.Error(s);
}

MOQ - Why is below not mocking?

Anyone have any idea why below still calls YesService?
It almost seems like IYesService is considered separate to YesService..
Any ideas?
public interface IYesService
{
string Hello();
}
public class YesService : IYesService
{
public string Hello()
{
return "Yes";
}
}
class Program
{
static void Main(string[] args)
{
var _mock = new Mock<IYesService>();
_mock.Setup(x => x.Hello()).Returns("No");
var service = new YesService();
var result = service.Hello();
Console.Write(result);
Console.ReadLine();
}
}
Because you don't use the _mock, you instantiate the YesService and call the method on that.
You can use mocking when you consume this interface:
public class ServiceUser
{
private IYesService _yesService;
public ServiceUser(IYesService yesService)
{
_yesService = yesService;
}
public string CallService()
{
return _yesService.Hello();
}
}
Then use it like this:
var serviceMock = new Mock<IYesService>();
serviceMock.Setup(x => x.Hello()).Returns("No");
var service = new ServiceUser(serviceMock.Object);
var result = service.CallService();
You are creating an instance of YesService (the concrete class) in your Main method. The var service line an be done away with, and the var result line should become:
var result = _mock.Object.Hello();
Try that...

How to do unit test a service which has wcf client called

public class RefDataProvider : IRefDataProvider
{
private const string REF_DATA_COUNTRIES = "CountryData";
public IEnumerable<CountryLookupDto> GetCountries()
{
//if in cache then get cached version
if (CacheManager.GetInstance.OCache.Contains(REF_DATA_COUNTRIES))
return CacheManager.GetInstance.GetTypedItem<IEnumerable<CountryLookupDto>>(REF_DATA_COUNTRIES);
//not in cache so get from dadtavase
using (var service = new CrmServiceClient())
{
try
{
IEnumerable<CountryLookupDto> countriesDto = service.LookupCountries("*");
bool addedToCache = CacheManager.GetInstance.AddItemWithExpiration(REF_DATA_COUNTRIES, countriesDto,
12);
if (!addedToCache) throw new Exception("Cannot add ref data to cache");
}
catch (Exception ex)
{
LoggingManager.GetInstance.Log("Error", ex, LoggingManager.LogLevel.Error);
throw;
}
finally
{
service.Close();
}
}
return CacheManager.GetInstance.GetTypedItem<IEnumerable<CountryLookupDto>>(REF_DATA_COUNTRIES);
}
}
Trying to do unit test onto the method. Having problem with wcf client call.
I am trying to verify CrmServiceClient() calls in unit test. Is there any way to test wcf calls in unit test. Please advise.
[TestFixture]
public class TestRefDataProvider
{
private IReferenceDataProvider _referenceDataProvider;
[SetUp]
public void SetUp()
{
_referenceDataProvider = new ReferenceDataProvider();
}
[Test]
public void Verify_GetCountries()
{
Assert.IsNotNull(_referenceDataProvider.GetCountries());
}
}
Thanks Ilya. After Ilya explains: I came out with this:
public class ReferenceDataProvider : IReferenceDataProvider
{
private const string REF_DATA_TITLE = "TitleData";
private const string REF_DATA_COUNTRIES = "CountryData";
private readonly ICrmService _crmService;
public ReferenceDataProvider(ICrmService crmService)
{
_crmService = crmService;
}
public IEnumerable<CountryLookupDto> GetCountries()
{
//if in cache then get cached version
if (CacheManager.GetInstance.OCache.Contains(REF_DATA_COUNTRIES))
return CacheManager.GetInstance.GetTypedItem<IEnumerable<CountryLookupDto>>(REF_DATA_COUNTRIES);
try
{
IEnumerable<CountryLookupDto> countriesDto = _crmService.LookupCountries("*");
bool addedToCache = CacheManager.GetInstance.AddItemWithExpiration(REF_DATA_COUNTRIES, countriesDto,
12);
if (!addedToCache) throw new Exception("Cannot add ref data to cache");
}
catch (Exception ex)
{
LoggingManager.GetInstance.Log("Error", ex, LoggingManager.LogLevel.Error);
throw;
}
return CacheManager.GetInstance.GetTypedItem<IEnumerable<CountryLookupDto>>(REF_DATA_COUNTRIES);
}
}
My question here is I had service.Close() before. Now I can't use it. Is that safe ?
If CrmServiceClient is your WCF service so you should have an interface ICrmServiceClient.
Therefore you should not create new instance of CrmServiceClient in your code. The only thing your need is a dependency on ICrmServiceClient (e.g. via constructor)
public class RefDataProvider : IRefDataProvider
{
private readonly ICrmServiceClient crmServiceClient;
public RefDataProvider(ICrmServiceClient crmServiceClient)
{
this.crmServiceClient = crmServiceClient;
}
public IEnumerable<CountryLookupDto> GetCountries()
{
/* your code */
}
}
In this case it is possible to inject mock ok ICrmServiceClient easily.
[TestFixture]
public class TestRefDataProvider
{
private Mock<ICrmServiceClient> crmServiceClientMock;
private IReferenceDataProvider _referenceDataProvider;
[SetUp]
public void SetUp()
{
crmServiceClientMock = new Mock<ICrmServiceClient>();
crmServiceClientMock
.Setuo(/* your code */)
.Returns(/* your code */);
_referenceDataProvider = new ReferenceDataProvider(
crmServiceClientMock.Object
);
}
}
MOQ framework is used in order to mock dependencies.

Categories