I have written following code snippet:
public interface IModelToViewModelServiceBase<in TDomain, out TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
TViewModel GetViewModel(TDomain model);
}
public abstract class ModelToViewModelServiceBase<TDomain, TViewModel> : IModelToViewModelServiceBase<TDomain, TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
private readonly IDictionary<TDomain, TViewModel> _modelToViewModel
= new Dictionary<TDomain, TViewModel>();
protected abstract TViewModel Create(TDomain model);
public TViewModel GetViewModel(TDomain model)
{
if (model == null) return null;
if (!_modelToViewModel.ContainsKey(model))
_modelToViewModel[model] = Create(model);
return _modelToViewModel[model];
}
}
The purpose of this class doesn't matter for the problem. On rare occasions I get a KeyNotFound on the return-line. However, to my understanding the preceding if-clauses should prevent this from happening. No key can possibly be null and the retrieved value if not existing before was added in the previous instruction.
What am I missing here?
I now developed a workaround:
public interface IModelToViewModelServiceBase<in TDomain, out TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
TViewModel GetViewModel(TDomain model);
}
public abstract class ModelToViewModelServiceBase<TDomain, TViewModel> : IModelToViewModelServiceBase<TDomain, TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
private readonly IDictionary<TDomain, TViewModel> _modelToViewModel
= new Dictionary<TDomain, TViewModel>();
protected abstract TViewModel Create(TDomain model);
public TViewModel GetViewModel(TDomain model)
{
if (model == null) return null;
TViewModel viewModel = null;
if (!_modelToViewModel.ContainsKey(model))
{
viewModel = Create(model);
_modelToViewModel[model] = viewModel;
}
else
viewModel = _modelToViewModel[model];
return viewModel;
}
}
This seems to work. However, this workaround shouldn't be necessary. May be this workaround is even better, because now one less access to the dictionary is executed. Still, the previous version should have worked always.
Update after answer:
@evk and @mjwills are both right. I didn't consider that my code is unsafe for concurrent use and multiple threads are accessing it. Hence, afer the suggestions of @mjwills the code looks as follows:
public interface IModelToViewModelServiceBase<in TDomain, out TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
TViewModel GetViewModel(TDomain model);
}
public abstract class ModelToViewModelServiceBase<TDomain, TViewModel> : IModelToViewModelServiceBase<TDomain, TViewModel>
where TDomain : class, IDataModel
where TViewModel : class, IDataModelViewModel
{
private readonly ConcurrentDictionary<TDomain, TViewModel> _modelToViewModel
= new ConcurrentDictionary<TDomain, TViewModel>();
protected abstract TViewModel Create(TDomain model);
public TViewModel GetViewModel(TDomain model)
{
if (model == null) return null;
return _modelToViewModel.GetOrAdd(model, Create); ;
}
}