Dynamically passing T into a method - c#

I have followed this post to create a writable options class for .Net Core WebAPI. I use this class for updating my appsettings.json file.
I want to create these writable options classes dynamically. For example, I have multiple options classes like OptionsA, OptionsB and so on. They can be configured in appsettings.json and should only be injected when they are present in that file.
So far so good, now my problem is ConfigureWritable has a type parameter T. My problem is, when my code finds OptionsA in the appsettings.json file, how do I supply the type to the ConfigureWritable method?
Here's what I have so far:
private void AddOptionalServices(IServiceCollection services, ServiceSettings serviceSettings)
{
foreach (var serviceSetting in serviceSettings.Services)
{
var serviceType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(t => t.GetTypes()).Where(t => t.Name == serviceSetting.Name).FirstOrDefault();
var settingsType = (Type)serviceType.GetProperty("ServiceType").GetValue(serviceType, null);
services.AddSingleton(typeof(IHostedService), serviceType);
services.ConfigureWritable<settingsType>(Configuration.GetSection("")); //Problem lies here
}
}
settingsType is a property that is returned from serviceType.
EDIT: Second attempt based on Lasse's comment:
private void AddOptionalServices(IServiceCollection services, ServiceSettings serviceSettings)
{
foreach (var serviceSetting in serviceSettings.Services)
{
var serviceType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(t => t.GetTypes()).Where(t => t.Name == serviceSetting.Name).FirstOrDefault();
var settingsType = (Type)serviceType.GetProperty("ServiceType").GetValue(serviceType, null);
services.AddSingleton(typeof(IHostedService), serviceType);
var method = typeof(IServiceCollection).GetMethod("ConfigureWritable"); //returns null
var methods = typeof(IServiceCollection).GetMethods(); //returns empty enumeration
var generic = method.MakeGenericMethod(settingsType);
generic.Invoke(this, null);
}
}
As you can see, I don't get the method back when using GetMethod.

Related

Invalid wire-type (String) when trying to grpc with protobuf-net grpc

I recently started receiving the following error on my protobuf-net grpc calls:
Grpc.Core.RpcException: 'Status(StatusCode=Internal, Detail="Error starting gRPC call: Invalid wire-type (String); this usually means you have over-written a file without truncating or setting the length; see https://stackoverflow.com/q/2152978/23354")'
I already went to that link, but didn't really find anything that really relates to what I am working on.
So, this error started popping up a while ago, when I did write some custom reflection code to do protobuf-net inheritance modeling dynamically via RuntimeTypeModel.
This is my code for this:
private void InitializeBindingsForGrpcService([NotNull] Type grpcService)
{
foreach (var method in grpcService.GetMethods())
{
var involvedTypes = method.GetParameters().Select(x => x.ParameterType).ToList();
involvedTypes.Add(method.ReturnType);
var cleanTypes = involvedTypes.Where(x => !x.IsGenericType).ToList();
var taskCleanTypes = involvedTypes.Except(cleanTypes).Select(x => x.CheckAndGetTaskWrappedType());
cleanTypes.AddRange(taskCleanTypes);
var genericTypes = cleanTypes.Where(x =>x.IsGenericType);
foreach (var genericType in genericTypes)
{
RegisterBaseChain(genericType);
}
var nonGenerics = cleanTypes.Where(x => !x.IsGenericType);
foreach (var type in nonGenerics)
{
if (!type.Namespace.StartsWith("System"))
{
RuntimeTypeModel.Default.Add(type, true);
}
}
}
}
private void RegisterBaseChain([NotNull] Type type)
{
var baseType = type.BaseType;
if (baseType == null || baseType == typeof(object))
{
return;
}
var baseMetaData = RuntimeTypeModel.Default.Add(baseType);
baseMetaData.AddSubType(m_protoIndex, type);
m_protoIndex++;
RegisterBaseChain(baseType);
}
So, I am now kind of curious where I might be going wrong with this. I'd love to provide more details for this case, but I just don't really know what to put in.
I did check whether all the relevant objects for the given call are properly registered, and that is the case.
Any pointers to what I should do here?

How to change input parameters using Action Filters

The background:
I am trying to fix a potential security issue to remove single quotes injected in as string parameters to my WebAPI method (this needs to be done throughout the application).
I tried to accomplish this by creating an Action Filter which does the necessary
public class ValidateActionParametersAttribute : ActionFilterAttribute, IActionFilter
{
public override void OnActionExecuting(HttpActionContext actionExecutedContext)
{
var parameters = actionExecutedContext.ActionArguments;
var parameterList = parameters.Values.ToList();
parameterList.Where(x => x.GetType() == typeof(string)).ToList().ForEach(y => y = y.ToString().Replace("\'", ""));
base.OnActionExecuting(actionExecutedContext);
}
}
And registered it globally in my WebApiConfig
config.Filters.Add(new ValidateActionParametersAttribute());
But when I checked after placing a breakpoint in the code the parameter changes done in the ActionFilter does not seem to reflect. Can someone guide me what I am doing wrong?
You are not updating the value in the arguments dictionary but you are only replacing the y parameter of the lambda function you pass to ForEach.
Since ActionArguments is a dictionary you can do the follwing:
var stringArgs = context.ActionArguments.Where(pair => pair.Value is string).ToList();
foreach (var keyValue in stringArgs)
{
var safeValue = ((string)keyValue.Value).Replace("\'", "");
context.ActionArguments[keyValue.Key] = safeValue;
}
This will get all arguments which are strings and replace them with the safe version.

Moq CreateInstance fails when constructor has dependencies using Func<T>

public class MyService
{
private readonly ISomething _something;
private readonly Func<IRarelyGetUsed> _rarelyGetUsed;
public MyService(ISomething something, Func<IRarelyGetUsed> rarelyGetUsed)
{
_something = something;
_rarelyGetUsed = rarelyGetUsed;
}
}
We use Autofac for our IOC and found we can get big performance gains (when under load) using the Func<T> approach because those dependencies don't get resolved until they are used, and in some scenarios certain dependencies are not used.
We are also using Moq for some unit testing.
var _container = new AutoMocker();
var _service = _container.CreateInstance<MyService>();
At this point it blows up - System.NullReferenceException : Object reference not set to an instance of an object.
Anyone know how to tell Moq to play nicely with Func dependencies?
Note that if I change Func<IRarelyGetUsed> to IRarelyGetUsed there's no exception.
Edit: Turns out the nuget package was pretty old - after updating the package https://github.com/tkellogg/Moq.AutoMocker this is now working.
However, there's one more problem to solve -
_container.GetMock<Func<IRarelyGetUsed>>().Setup(p => p().DoSomething(It.IsAny<string>())).Returns(true).Verifiable();
Trying to setup the result of the above method result in - Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.InvocationExpression'
Edit 2:
var serviceMock = _container.GetMock<IRarelyGetUsed>();
serviceMock.Setup(r => r.DoSomething()).Returns(someData);
_container.GetMock<Func<IRarelyGetUsed>>().Setup(s => s()).Returns(serviceMock.Object);
The above now works, however it requires setting up both the Func<IRarelyGetUsed> and IRarelyGetUsed - would be nice if it was only necessary to do one, otherwise there's more overhead per test.
You can automatically wire up a Func<T> for every T with AutoMocker doing something like this:
public void RegisterFuncs(AutoMocker autoMocker, IEnumerable<Type> types)
{
var use = typeof(AutoMocker).GetMethods()
.First(t => t.Name == "Use" &&
t.GetGenericArguments().First().Name == "TService");
var get = typeof(AutoMocker).GetMethod("Get");
foreach (var type in types)
{
// _.container.Use<Func<T>>()
var typedUse = use.MakeGenericMethod(typeof(Func<>).MakeGenericType(type));
// _container.Get<T>()
var typedGet = get.MakeGenericMethod(type);
var target = Expression.Constant(autoMocker);
var call = Expression.Call(target, typedGet);
// () => _container.Get<T>()
var lambda = Expression.Lambda(call);
// _.container.Use<Func<T>>(() => _container.Get<T>())
typedUse.Invoke(autoMocker, new object[] { lambda.Compile() });
}
}
// Then call with your AutoMocker instance and the interfaces you want to wire up
var types = typeof(SomeNamespace.ISomeInterface).Assembly.GetExportedTypes()
.Where(t => t.IsInterface && !t.ContainsGenericParameters);
RegisterFuncs(yourAutoMocker, types);
Run this in your test setup just after creating a container.
Note: to make the above work for Lazy<T>, you have to instantiate the Lazy<T> with a Func<T>, so you'll need something like the following:
public void RegisterLazys(AutoMocker autoMocker, IEnumerable<Type> types)
{
var use = typeof(AutoMocker).GetMethods()
.First(t => t.Name == "Use" &&
t.GetGenericArguments().First().Name == "TService");
var get = typeof(AutoMocker).GetMethod("Get");
foreach (var type in types)
{
// Lazy<T>
var lazyT = typeof(Lazy<>).MakeGenericType(type);
// _.container.Use<Lazy<T>>()
var typedUse = use.MakeGenericMethod(lazyT);
// _container.Get<T>()
var typedGet = get.MakeGenericMethod(type);
var target = Expression.Constant(autoMocker);
var call = Expression.Call(target, typedGet);
// () => _container.Get<T>()
var lambda = Expression.Lambda(call);
// _.container.Use<Lazy<T>>(new Lazy<T>(() => _container.Get<T>()));
typedUse.Invoke(autoMocker, new object[] { Activator.CreateInstance(lazyT, lambda.Compile()) });
}
}
Have you tried using Lazy<T> instead of Func<T> to achieve the lazy loading you desire? It may play better with Moq than Func does.
Documentation on Lazy

Unit Test Using Moq

I am unit-testing an async method that returns a List<T>. This method has a dependency on a mapping class/interface. In my unit-test, I am mocking the mapping class using moq. The test runs okay, and the returned list has items, but the values of the items is null. I think the problem is because I haven't stubbed-out the mapping classes methods properly. I don't have a lot of experience with testing, so any guidance is appreciated.
Test Method:
[TestMethod]
[TestCategory("CSR.Data.Tests.Services.ServiceSearchTest")]
public void SearchAccount()
{
// Arrange
var mapper = new Mock<CSR.Data.Mapping.Interfaces.IMapper<Account, AccountDTO>>();
mapper.Setup(i => i.Initialize());
mapper.Setup(i => i.ToDomain(It.IsAny<AccountSearchResult>())).Returns(It.IsAny<Account>);
mapper.Setup(i => i.DomainToDto(It.IsAny<Account>())).Returns(It.IsAny<AccountDTO>);
var service = new ServiceSearch(null,mapper.Object);
string accountNumber = "123";
string accountName = "";
// Act
var results = service.SearchAccount(accountNumber, accountName);
// Assert
Assert.IsTrue(results.Result.Count >= 1);
}
Method/Class That I'm Testing:
public class ServiceSearch : IServiceSearch
{
public ServiceSearch(IMapper<Claim, ClaimDTO> claimMapper, IMapper<Account, AccountDTO> accountMapper)
{
_claimMapper = claimMapper;
_accountMapper = accountMapper;
}
public async Task<List<AccountDTO>> SearchAccount(string accountNumber, string accountName)
{
var accounts = new List<Account>();
var accountDTOs = new List<AccountDTO>();
var results = await Task.Run(() => base.AccountSearch(accountNumber, accountName).Result);
if (results != null && results.Count > 0)
{
//Map DH to Domain
_accountMapper.Initialize();
foreach (AccountSearchResult result in results)
{
accounts.Add(_accountMapper.ToDomain(result));
}
//Map Domain to DTO
foreach (Account account in accounts)
{
accountDTOs.Add(_accountMapper.DomainToDto(account));
}
}
return accountDTOs;
}
}
This isn't the best place to use a Mock object because you are going to spend a lot of time writing your test objects and mock results. The issue with the setup call is that you haven't configured anything to send back in the result. A correct example would be:
// you would fully configure this object
AccountDTO expectedResult = new AccountDTO();
mapper.Setup(i => i.ToDomain(It.IsAny<AccountSearchResult>())).Returns(expectedResult);
Now you can use the setup to configure different accountDTOs for different inputs.
You call also configure a callback to generate the account at test time:
mapper.Setup(i => i.ToDomain(It.IsAny<AccountSearchResult>())).Returns<AccountSearchResult>(sr => {
// build and return your dto here
});
However, unless your mapper is expensive to run or create, I think you'd better off just ensure that it is fully tested and acceptable and then use it to go ahead and generate the DTOs directly instead of trying to mock it out.
You don't actually setup an object in the ".Returns" call. You need to make sure to setup the ".Returns" to actually have an object with values.

NHibernate ClassMap<T> code not executing

I'm setting up a new project and have gotten NHibernate to work with structuremap...sorta. I'm using the NHibernate.Mapping.ByCode.Conformist setup with ClassMaps. No errors occur, but when I query over a session and there are records present in the database for a particular type, nothing comes back. Upon further examination, it appears that the mappings that I've set up for these types are not executing. Here's my code that wires up things for structuremap. I can confirm that it is being executed.
public class OrmRegistry : Registry
{
public OrmRegistry()
{
var sessionFactory = BuildSessionFactory();
For<ISessionFactory>().Singleton().Use(sessionFactory);
For<ISession>().HybridHttpOrThreadLocalScoped().Use(s => sessionFactory.OpenSession());
}
public ISessionFactory BuildSessionFactory()
{
var cfg = new Configuration().DataBaseIntegration(db =>
{
db.ConnectionStringName = "LocalSqlServer";
db.Dialect<MsSql2008Dialect>();
db.Driver<SqlClientDriver>();
db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
db.IsolationLevel = IsolationLevel.ReadUncommitted;
db.BatchSize = 500;
}).AddAssembly(Assembly.GetExecutingAssembly());
if(HttpContext.Current != null)
{
cfg.CurrentSessionContext<WebSessionContext>();
}
else
{
cfg.CurrentSessionContext<ThreadStaticSessionContext>();
}
return cfg.BuildSessionFactory();
}
}
I'm nearly certain I'm just missing something extremely obvious here, but I've been looking at it for a few hours and haven't had any success. I also got downsized a couple days ago, so I don't have a coworker around to look at it.
Looks like you got your configuration initialized, but what about mapping? You need to initialize mappings like this (if you are using conventions):
var mapper = new ConventionModelMapper();
// TODO: define conventions here using mapper instance
// just an example on how I have been using it
var entities = ... // get all entity types here
cfg.AddDeserializedMapping(mapper.CompileMappingFor(entities), "MyMappings");
return cfg.BuildSessionFactory();
And another example if you are using mapping classes (from this post):
var mapper = new ModelMapper();
var mappingTypes = typeof (InvoiceMapping).Assembly.GetExportedTypes()
.Where(t => t.Name.EndsWith("Mapping")).ToArray();
mapper.AddMappings(mappingTypes);
cfg.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
return cfg.BuildSessionFactory();

Categories