I have a class which contains readonly private fields
public sealed class Company : AggregateRoot
{
private readonly List<TimeSeriesImportInfo> _executedImportInfo = new();
private readonly List<StockPrice> _stockPrices = new();
public Company(
Guid id,
string name,
string symbol)
: base(id)
{
Name = name;
Symbol = symbol;
}
public string Name { get; }
public string Symbol { get; }
public IReadOnlyCollection<TimeSeriesImportInfo> ExecutedImportInfo => _executedImportInfo.AsReadOnly();
public IReadOnlyCollection<StockPrice> StockPrices => _stockPrices.AsReadOnly();
public void AddExecutedImportInfo(DateTime month)
{
var timeSeriesImportInfo = new TimeSeriesImportInfo(month);
var hasAlreadyBeenHandled = ExecutedImportInfo.Contains(timeSeriesImportInfo);
if (hasAlreadyBeenHandled)
{
throw new ImportExecutedBeforeDomainException(Id, month);
}
_executedImportInfo.Add(timeSeriesImportInfo);
var importExecutedEvent = new ImportExecutedDomainEvent(Id, timeSeriesImportInfo.Month);
Enqueue(importExecutedEvent);
}
public void AddStockPrices(ICollection<StockPrice> stockPrices)
{
var hasAlreadyBeenImported = stockPrices.Any(newStockPrice =>
_stockPrices.Contains(newStockPrice));
if (hasAlreadyBeenImported)
{
throw new StockPriceAlreadyImportedDomainException(Id, stockPrices);
}
_stockPrices.AddRange(stockPrices);
var stockPricesAddedEvent = new StockPricesAddedDomainEvent(Id, stockPrices);
Enqueue(stockPricesAddedEvent);
}
}
I have configured the mapping for the company using the following method:
public static class Mapping
{
public static IServiceCollection AddMappings(this IServiceCollection services)
{
BsonClassMap.RegisterClassMap<Company>(classMap =>
{
classMap.AutoMap();
classMap.MapField("_executedImportInfo").SetElementName(nameof(Company.ExecutedImportInfo));
classMap.MapField("_stockPrices").SetElementName(nameof(Company.StockPrices));
});
return services;
}
}
The values of those private readonly fields are correctly saved to the database. However when im trying to fetch company object - those private fields can't be deserialized properly, because after the fetch they have default values (although they are persisted in the db). If i removed the "readonly" keyword from them - everything works correctly. I would like to avoid removing the readonly key and would like to not modify the Company class. How to achieve that in C#? Thanks for any help, regards.
it's not supported, readonly fields are skipped during deserialization after ctor called.
Related
I am trying to use a Singleton backed with an XML file for my application configurations.
My Singleton looks like this:
public sealed class AppConfig
{
private static readonly Lazy<AppConfig> appConfig = new(() => new AppConfig());
public static AppConfig Settings
{
get { return appConfig.Value; }
}
private AppConfig()
{
}
[XmlElement]
public string SomeValue { get; set; }
}
To write the contents of the Singleton to disk I can use:
var xs = new XmlSerializer(typeof(AppConfig));
var sw = new StreamWriter(fileName);
xs.Serialize(sw, AppConfig.Settings);
The obvious code to read the XML file back into the Singleton results in a compile error, because of course the instance is readonly
var sr = new StreamReader(fileName);
AppConfig.Settings = (AppConfig)xs.Deserialize(sr);
Error CS0200
Property or indexer 'AppConfig.Settings' cannot be assigned to -- it is read only
An option I have found is to create another class that holds the values and make this a property of the singleton. Then this class gets serialized, eg:
public sealed class AppConfig
{
private static readonly Lazy<AppConfig> appConfig = new(() => new AppConfig());
public static AppConfig Settings
{
get { return appConfig.Value; }
}
private AppConfig()
{
Values = new Values();
}
public Values Values { get; set; }
}
public class Values
{
[XmlElement]
public string SomeValue { get; set; }
}
To serialise/deserialise use the appropriate call on this, eg:
AppConfig.Settings.Values = (Values)xs.Deserialize(sr);
While what I have come up with works, it seems a bit convoluted, and the access path to the variables is of course longer.
Is there a better way to deserialise into the Singleton?
Is it a safe option just to remove the readonly?
I've got options like this:
public class ApplicationSettings
{
public string Title { get; set; }
public string PluginFolders { get; set; }
}
and services like this:
public interface IWildcardResolver
{
string Resolve(string value);
}
public class WildcardResolver : IWildcardResolver
{
private readonly IHostingEnvironment _hostingEnvironment;
public WildcardResolver(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
AddWildcard("%contentRootPath%", _hostingEnvironment.ContentRootPath);
AddWildcard("%webRootPath%", _hostingEnvironment.WebRootPath);
AddWildcard("%environment%", _hostingEnvironment.EnvironmentName);
}
private readonly Dictionary<string, string> _hardWiredValues = new Dictionary<string, string>();
/// <inheritdoc />
public string Resolve(string value)
{
var sb = new StringBuilder(value);
foreach (var pair in _hardWiredValues)
{
sb.Replace(pair.Key, pair.Value);
}
return sb.ToString();
}
public void AddWildcard(string name, string value)
{
if (_hardWiredValues.ContainsKey(name))
throw new Exception($"A value for the wildcard {name} already exists.");
_hardWiredValues.Add(name, value);
}
}
How can i make sure that before i access those settings through DI with IOptions<AppSettings> PluginFolders is translated (because it contains wildcards)? I've tried IConfigureOptions<AppSettings> and IPostConfigureOptions<AppSettings> but both of them appear to happen at a stage too late. it's like i am missing a IPreConfigureOptions or something.
public class PluginManager
{
private readonly IOptions<ApplicationSettings> _settings;
public PluginManager(IOptions<ApplicationSettings> settings)
{
_settings = settings;
// how do i get an instance here which makes sure that the ApplicationSettings.PluginPaths is already manipulated without doing it manually?
}
}
Doing it like this works, but then it feels like i am fighting the framework since i can't use IOptions<AppSettings> like everywhere else:
Ok. I found it out by digging through some microsoft component sources.
This is the solution:
public class ApplicationSettingsSetup : IConfigureOptions<ApplicationSettings>
{
private readonly IWildcardResolver _wildcardResolver;
public ApplicationSettingsSetup(IWildcardResolver wildcardResolver)
{
_wildcardResolver = wildcardResolver;
}
/// <inheritdoc />
public void Configure(ApplicationSettings options)
{
options.PluginFolders = _wildcardResolver.Resolve(options.PluginFolders);
}
}
Registration:
services.AddTransient<IConfigureOptions<ApplicationSettings>, ApplicationSettingsSetup>();
services.Configure<ApplicationSettings>(Configuration.GetSection("ApplicationSettings"));
Previously i was loading the AppSettings from the Configuration and registering IConfigurationOptions afterwards. Somehow i assumed the factory which creates Options would know to call IConfigureOptions first before returning the IOptions instance - this is wrong.
Changing the order fixed it.
This looks more along the lines of what you are trying to achieve
services.AddOptions<ApplicationSettings>()
.Configure<IWildcardResolver>((options, wildcardResolver) => {
options.PluginFolders = wildcardResolver.Resolve(options.PluginFolders);
//...
});
The above registers an action used to configure a particular type of options. Note: These are run before all .
Reference Use DI services to configure options
I have Core in which Interface is declared as
public interface IRequestProvider
{
int SomeId { get; set; }
}
Implementation also define in same layer
and then I have another layer Repo layer in which I am calling another external nuget packages called DataAccess layer
in which I have declared
public interface IRequestProvider
{
int SomeId { get; set; }
int SomeOtherId { get; set; }
}
so In core and DataAccess both layer I have defined IRequestProvider
Lamar code
public static class SomeRegistry
{
public static void RegisterDISome(this ServiceRegistry services, IConfigurationRoot configurationRoot)
{
services.For<IRequestProvider>().Use<RequestProvider>().Scoped();
services.For<DataAccessInterfaces.IRequestProvider>().Use<DataAccessModel.RequestProvider>().Scoped();
}
}
Scoped use to pass the same instance throughout the request
Automapper is enable
public class DomainToRepoMappingsProfile : Profile
{
public DomainToRepoMappingsProfile()
{
this.CreateMap<IRequestProvider, DataAccess.IRequestProvider>()
.ForMember(dst => dst.SomeOtherId, opt => opt.Ignore());
}
}
My expectation is when I change something in Core.IRequestProvider from any layer it should auto reflected in DataAccess.IRequestProvider layer
Currently I am calling IDomainToRepoMappingRequestProvider.map() each time to set DataAccess.IRequestProvider
public class DomainToRepoMappingRequestProvider : IDomainToRepoMappingRequestProvider
{
private readonly IMapper _mapper = null;
private readonly IRequestProvider _requestProvider = null;
private DataAccess.IRequestProvider _dataAccessRequestProvider = null;
public DomainToRepoMappingRequestProvider(IRequestProvider requestProvider, DataAccess.IRequestProvider dataAccessRequestProvider, IMapper mapper)
{
_mapper = mapper;
_requestProvider = requestProvider;
_dataAccessRequestProvider = dataAccessRequestProvider;
}
public void Map()
{
_mapper.Map(_requestProvider, _dataAccessRequestProvider);
}
}
I finding a solution to reflect changes automatically when something is changed without calling map()
How about having a property setter in the implementation of IRequestProvider call the mapper for you? The property getters and setters can be used to do much more than just setting a private backing field. An example:
public class RequestProvider : IRequestProvider
{
private readonly _mappingProvider;
private int _someId;
public RequestProvider(IDomainToRepoMappingRequestProvider mappingProvider)
{
_mappingProvider = mappingProvider
}
public int SomeId
{
get;
set
{
_someId = value;
_mappingProvider.Map();
}
}
}
I have about 15 entities for which I have to get id and name properties. First, I check whether the properties can be pulled from local cache. If not, then I fetch them by code from DB and save in cache.
Here's my code just for two entities:
public class GetParamsService : IGetParamsService
{
private readonly IMemoryCache _cache;
private readonly MemoryCacheEntryOptions _cacheOptions;
private readonly IDealTypeRepository _dealTypeRepository;
private readonly ICurrencyRepository _currencyRepository;
public GetParamsService(IMemoryCache memoryCache,
IDealTypeRepository dealTypeRepository,
ICurrencyRepository currencyRepository)
{
_cache = memoryCache;
_cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromHours(2));
_dealTypeRepository = dealTypeRepository;
_currencyRepository = currencyRepository;
}
public async Task<(int idDealType, string dealTypeName)> GetDealTypeParams(
string dealTypeCode)
{
if (!_cache.TryGetValue(CacheKeys.IdDealType, out int idDealType)
| !_cache.TryGetValue(CacheKeys.DealTypeName, out string dealTypeName))
{
var dealType = await _dealTypeRepository
.Get(x => x.Code == dealTypeCode, dealTypeCode);
idDealType = dealType.IdDealType;
dealTypeName = dealType.Name;
_cache.Set(CacheKeys.IdDealType, idDealType, _cacheOptions);
_cache.Set(CacheKeys.DealTypeName, dealTypeName, _cacheOptions);
}
return (idDealType, dealTypeName);
}
public async Task<(int idCurrency, string currencyName)> GetCurrencyParams(
string currencyCode)
{
if (!_cache.TryGetValue(CacheKeys.IdCurrency, out int idCurrency)
| !_cache.TryGetValue(CacheKeys.CurrencyName, out string currencyName))
{
var currency = await _currencyRepository
.Get(x => x.Code == currencyCode, currencyCode);
idCurrency = currency.IdCurrency;
currencyName = currency.Name;
_cache.Set(CacheKeys.IdCurrency, idCurrency, _cacheOptions);
_cache.Set(CacheKeys.CurrencyName, currencyName, _cacheOptions);
}
return (idCurrency, currencyName);
}
}
So, the methods GetDealTypeParams and GetCurrencyParams are pretty much the same and I want to make one generic method instead of many similar methods. I guess it makes sense.
The problem is that I don't know how to get "the right properties for given entity" in CacheKeys class:
public static class CacheKeys
{
public static string IdDealType => "_IdDealType";
public static string DealTypeName => "_DealTypeName";
public static string IdCurrency => "_IdCurrency";
public static string CurrencyName => "_CurrencyName";
// ...
}
Every repository is inheritted from GenericRepository with Get method:
public class DealTypeRepository : GenericRepository<DealTypeEntity>, IDealTypeRepository
{
public DealTypeRepository(DbContextOptions<MyContext> dbContextOptions)
: base(dbContextOptions)
{
}
}
public class GenericRepository<TEntity> where TEntity : class
{
private readonly DbContextOptions<MyContext> _dbContextOptions;
public GenericRepository(DbContextOptions<MyContext> dbContextOptions)
{
_dbContextOptions = dbContextOptions;
}
public async Task<TEntity> Get(Expression<Func<TEntity, bool>> predicate, string code)
{
try
{
using (var db = new MyContext(_dbContextOptions))
{
using (var tr = db.Database.BeginTransaction(
IsolationLevel.ReadUncommitted))
{
var entity = await db.Set<TEntity>().AsNoTracking()
.FirstAsync(predicate);
tr.Commit();
return entity;
}
}
}
catch (Exception e)
{
throw new Exception("Error on getting entity by code: {code}");
}
}
}
Can you please guide me how can I retrieve the needed properties of CacheKeys class to write a generic method ? I guess it can be easily done with reflection.
UPDATE:
I'm not sure whether I have to try one generic method as every entity has Id property with its own name (for example, IdDealType for dealType, IdCurrency for currency)
Before I start: This solution assumes that all your entities follow the naming conventions you showed on your sample code.
First, it would be better if you had a repository exclusive to this service, where it would be possible to query by any entity type. There, you would use your convention to get those properties names, and you could query them by using EF.Property. As all your queries seem to be on the Code column, we can also simplify the parameters on that repo's method.
public class ParamRepository : IParamRepository
{
private readonly DbContextOptions<MyContext> _dbContextOptions;
public ParamRepository(DbContextOptions<MyContext> dbContextOptions)
{
_dbContextOptions = dbContextOptions;
}
public async Task<(int id, string name)> GetParamsByCode<TEntity>(string code) where TEntity : class
{
string entityName = typeof(TEntity).Name;
string idProp = $"Id{entityName}";
string nameProp = $"{entityName}Name";
try
{
using (var db = new MyContext(_dbContextOptions))
{
var entity = await db.Set<TEntity>().AsNoTracking()
.Where(p => EF.Property<string>(p, "Code") == code)
.Select(p => new { Id = EF.Property<int>(p, idProp), Name = EF.Property<string>(p, nameProp)})
.FirstAsync();
return (id: entity.Id, name: entity.Name);
}
}
catch (Exception e)
{
throw new Exception("Error on getting entity by code: {code}");
}
}
}
You would also need to refactor your cache keys to be created from conventions:
public static class CacheKeys
{
public static string GetIdKey<TEntity>() => $"_Id{typeof(TEntity).Name}";
public static string GetNameKey<TEntity>() => $"_{typeof(TEntity).Name}Name";
}
Then, it gets easy on the GetParamsService:
public class GetParamsService
{
private readonly IMemoryCache _cache;
private readonly MemoryCacheEntryOptions _cacheOptions;
private readonly IParamRepository _paramRepository;
public GetParamsService(IMemoryCache memoryCache,
IParamRepository paramRepository)
{
_cache = memoryCache;
_cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromHours(2));
_paramRepository = paramRepository;
}
public async Task<(int id, string name)> GetParams<TEntity>(string code) where TEntity : class
{
string cacheIdKey = CacheKeys.GetIdKey<TEntity>();
string cacheNameKey = CacheKeys.GetNameKey<TEntity>();
if (!_cache.TryGetValue(cacheIdKey, out int cacheId)
| !_cache.TryGetValue(cacheNameKey, out string cacheName))
{
var param = await _paramRepository.GetParamsByCode<TEntity>(code);
cacheId = param.id;
cacheName = param.name;
_cache.Set(cacheIdKey, cacheId, _cacheOptions);
_cache.Set(cacheNameKey, cacheName, _cacheOptions);
}
return (cacheId, cacheName);
}
}
I have an interface for holding the connection configuration info for web service access:
public interface IServiceConnectionConfiguration
{
string Username { get; }
string Password { get; }
string ChannelEndpointUrl { get; }
string MediaEndpointUrl { get; }
string PlayerlEndpointUrl { get; }
string PlaylistEndpointUrl { get; }
}
I have a factory class that returns the service instance specific to the type of the service requested.
public static class ServiceClientFactory
{
public static void Configure(IServiceConnectionConfiguration config)
{
_config = config;
}
public static T GetService<T>() where T : class, IServiceClient
{
}
}
The factory is called as
Channel channelService = factory.GetService<Channel>();
What I am trying to figure out is an elegant way for the Factory code to resolve the endpoint urls for the passed in types itself based on the config object passed during initialization. eg. If the type parameter passed is channel, it should take the ChannelEndpointUrl while constructing the ChannelService.
I thought about using attributes on the config class to decorate the endpoint urls with the service type that they correspond to but it seems like a bad design.
Any ideas.
Well, one way to approach it would be to have the Factory have a private static Dictionary containing your initialization logic, indexed by "Type". Similar to a strategy pattern.
for example:
public static class ServiceClientFactory
{
private static IServiceConnectionConfiguration _config;
private static readonly Dictionary<Type, Func<IServiceClient>> Initializers = new Dictionary<Type, Func<IServiceClient>>();
static ServiceClientFactory()
{
Initializers.Add(typeof(Channel), () =>
{
return //create the service client based on the endpoint
});
}
public static void Configure(IServiceConnectionConfiguration config)
{
_config = config;
}
public static T GetService<T>() where T : class, IServiceClient
{
return (T)Initializers[typeof (T)]();
}
}
EDIT: Now, as you mentioned, you cannot instantiate explicitly in your factory since you'd cause a circular reference, maybe you can force a new() constraint, and construct the instance in the GetService method, and only use the dictionary for endpoint configuration, such as:
public static class ServiceClientFactory
{
private static IServiceConnectionConfiguration _config;
private static readonly Dictionary<Type, Action<IServiceClient>> Initializers = new Dictionary<Type, Action<IServiceClient>>();
static ServiceClientFactory()
{
Initializers.Add(typeof(Channel), t =>
{
t.Url = _config.ChannelEndpointUrl;
//configure t using the appropriate endpoint
});
}
public static void Configure(IServiceConnectionConfiguration config)
{
_config = config;
}
public static T GetService<T>() where T : class, IServiceClient, new()
{
var service = new T();
Initializers[typeof(T)](service);
return service;
}
}