I have a very strange problem. In my MVC 4 application I have this code to initialize StructureMap :
public static class IoC {
public static IContainer Initialize() {
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
x.For<IRestHttpClient>().Use<AtlamHttpClient>().Ctor<string>().Is(Settings.AtlamServicesURL);
});
ObjectFactory.AssertConfigurationIsValid();
return ObjectFactory.Container;
}
}
that works as expected. However, I also have a .NET 4.5 Web forms application with the same basic initialization code:
public static class IoC
{
public static IContainer Configure()
{
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.AssemblyContainingType<IRestHttpClient>();
scan.AssemblyContainingType<MessagePackMediaTypeFormatter>();
});
x.For<IRestHttpClient>().Use<AtlamHttpClient>().Ctor<string>().Is(Settings.BaseServiceUrl);
/*x.SetAllProperties(y =>
{
y.OfType<IRestHttpClient>();
});*/
});
ObjectFactory.AssertConfigurationIsValid();
return ObjectFactory.Container;
}
}
that throws an exception on AssertConfigurationIsValid() and is failing here in the AtlamHttpClient:
public static List<ContentNegotiator> extensionMappings = new List<ContentNegotiator>()
{
new ContentNegotiator("xml", "application/xml", new XmlMediaTypeFormatter()),
new ContentNegotiator("json", "application/json", new JsonMediaTypeFormatter()),
new ContentNegotiator("pack", "application/x-msgpack", new MessagePackMediaTypeFormatter())
};
which in turn calls:
public MessagePackMediaTypeFormatter()
{
MediaTypeHeaderValue val = MediaTypeHeaderValue.Parse(mime);
SupportedMediaTypes.Add(val);
}
and fails with an ArrayTypeMismatchException. Can't figure out why the first project works fine and the second one is failing. Any help would be greatly appreciated.
Finally figured it out. Turns out the problem was that I had a binding redirect to version 2.0 of System.Net.Http. Removed that and everything worked fine.
Related
I have a function app connected with an application insights instance.
When I look at the requests on application insights, all entries have a resultCode of 0, regardless of whether it was successful or not. How can I have the resultCode showing properly?
If I get it correctly, my function app is running at the version "3.0.14916.0".
Here is my startup:
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddLogging(loggingBuilder =>
{
var key = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
loggingBuilder.AddApplicationInsights(key);
});
builder.Services.AddSingleton(sp =>
{
var key = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
return new TelemetryConfiguration(key);
});
(...)
}
}
Edit 1:
In the comments it was asked why I am adding logging in Startup. I do it because, as far as I could verify, ILogger < MyClass > only logs to AI if I add logging in Startup.
Following is an example of an injected class. Note that this class is also used in other projects.
public class CosmosDbService : ICosmosDbService
{
private readonly IDocumentClient _documentClient;
private readonly ILogger _logger;
public CosmosDbService(IDocumentClient documentClient, ILogger<CosmosDbService> logger)
{
_logger = logger;
_documentClient = documentClient;
}
public async Task<UserData> GetUserAsync()
{
try
{
// Getting user here
// (...)
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching user.");
throw;
}
}
}
This class is injected as:
builder.Services.AddSingleton<IDocumentClient>(sp =>
{
// This does not really matter for this question
var configuration = sp.GetService<IConfiguration>();
var connectionString = configuration.GetValue<string>("COSMOS_DB_CONNECTION");
var cosmosDbConnectionString = new CosmosDbConnectionString(connectionString);
return new DocumentClient(cosmosDbConnectionString.ServiceEndpoint, cosmosDbConnectionString.AuthKey);
});
builder.Services.AddSingleton<ICosmosDbService, CosmosDbService>();
This answer from #PeterBons helped me fixing the wrong resultCode as well.
Basically I was importing the wrong package: Microsoft.Extensions.Logging.ApplicationInsights
I changed it to Microsoft.Azure.WebJobs.Logging.ApplicationInsights and removed the code in Startup. Now I got the resultCode properly filled in again.
I am trying to follow DryIoc and IServiceProvider on Prism for Xamarin.Forms (DryIoc.Microsoft.DependencyInjection) however I am using RefitClient for IHttpClientFactory
containerRegistry.RegisterServices(services =>
{
services.AddTransient<HttpLoggingHandler>();
services.AddTransient<AuthorizationDelegatingHandler>();
services.AddRefitClient<IMyApi>()
.ConfigureHttpClient(c =>
c.BaseAddress =
new Uri(apiBaseUrl))
.AddHttpMessageHandler<AuthorizationDelegatingHandler>()
.AddHttpMessageHandler<HttpLoggingHandler>()
.AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
{
TimeSpan.FromMilliseconds(300),
TimeSpan.FromSeconds(600),
TimeSpan.FromSeconds(800)
}))
.AddTransientHttpErrorPolicy(
p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
});
I have added
protected override IContainerExtension CreateContainerExtension() => PrismContainerExtension.Current;
When I try to make a request with IMyApi.
BaseAddress must be set on the HttpClient instance
at Refit.RequestBuilderImplementation+<>c__DisplayClass14_0`2[T,TBody].<BuildCancellableTaskFuncForMethod>b__0 (System.Net.Http.HttpClient client, System.Threading.CancellationToken ct, System.Object[] paramList) [0x00030] in /_/Refit/RequestBuilderImplementation.cs:236
I personally had to deal with all of that and ended up by creating Apizr where auth and logging handlers are built in, policies are resolved from registry and many more features like connectivity test, caching or prioritizing. If it could help.
I'm using Unity rather than Dryloc but the solution is the same.
The key seems very much to depend on installing the correct packages. Install ONLY these:
Prism.Forms.Extended
Prism.Unity.Extensions
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
//Omitted Code
containerRegistry.RegisterServices(serviceCollection =>
{
serviceCollection.AddHttpClient<IApiService, ApiService>(client =>
{
client.BaseAddress = new Uri("Your Address Here");
});
});
}
public class ApiService : IApiService
{
Func<IApi> _createClient;
public ApiService(HttpClient client)
{
_createClient = () =>
{
return RestService.For<IApi>(client, new RefitSettings
{
ContentSerializer = new NewtonsoftJsonContentSerializer()
});
}
}
public IApi GetApi()
{
return new Lazy<IApi>(() => _createClient()).Value;
}
}
In part also provided by this post:
https://xamgirl.com/consuming-restful-web-service-xamarin-forms-using-refit-part-2/
I've got a Prism application with DryIoc as container.
I'd like IHttpClientFactory to provide HttpClients to my typed clients, which are like this:
public class ExampleService : IExampleService
{
private readonly HttpClient _httpClient;
public RepoService(HttpClient client)
{
_httpClient = client;
}
public async Task<IEnumerable<string>> GetExamplesAsync()
{
// Code deleted for brevity.
}
}
In App.xaml.cs I register my typed client so they can be injected in viewmodels with the following:
public partial class App
// ...
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// Code deleted for brevity.
containerRegistry.Register<IExampleService, ExampleService>();
}
And that's before trying to use IHttpClientFactory.
Now, to add it, we should AddHttpClient() on IServiceCollection. That's where I thought DryIoc.Microsoft.DependencyInjection was needed, so, still in App.xaml.cs, I wrote the following:
public partial class App
// ...
protected override IContainerExtension CreateContainerExtension()
{
var services = new ServiceCollection();
services.AddHttpClient<IExampleService, ExampleService>(c =>
{
c.BaseAddress = new Uri("https://api.example.com/");
});
var container = new Container(CreateContainerRules())
.WithDependencyInjectionAdapter(services);
return new DryIocContainerExtension(container);
}
The problem is that in my ExampleService I'm getting client with the following specs:
{
"DefaultRequestHeaders":[
],
"BaseAddress":null,
"Timeout":"00:01:40",
"MaxResponseContentBufferSize":2147483647
}
whilst I expected BaseAddress to be https://api.example.com/, so the REST API call fails.
What is the correct pattern to use IServiceProvider when using Prism for Xamarin.Forms with DryIoc? Unfortunately there's no documentation or open source code available on the following matter, and I am kind of lost.
Thanks you, and have a great day.
UPDATE #1
As per kind Dan S. guidance, DryIoc.Microsoft.DependencyInjection was uninstalled so the project came back at its state before trying to use IServiceCollection dependencies (in my case, IHttpClientFactory), then I installed Prism.Forms.Extended and later Prism.DryIoc.Extensions.
After that CreateContainerExtension() in App.xaml.cs became:
protected override IContainerExtension CreateContainerExtension()
{
var containerExtension = PrismContainerExtension.Current;
containerExtension.RegisterServices(s =>
{
s.AddHttpClient<IExampleService, ExampleService>(c =>
{
c.BaseAddress = new Uri("https://api.example.com/");
});
});
return containerExtension;
}
and containerRegistry.Register<IExampleService, ExampleService>(); was removed from RegisterTypes().
Now ExampleService finally gets its HttpClient injected and everything is working.
UPDATE #2
The only packages related to Prism I am using are Prism.DryIoc.Forms and Prism.DryIoc.Extensions.
I completely removed the override of CreateContainerExtension() in App.xaml.cs and refactored RegisterTypes() to
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// Code deleted for brevity.
containerRegistry.RegisterServices(s =>
{
s.AddHttpClient<IExampleService, ExampleService>(c =>
{
c.BaseAddress = new Uri("https://api.example.com/");
});
});
}
This way I get thrown a NotImplementedException.
However, by overriding CreateContainerExtension() with the following:
protected override IContainerExtension CreateContainerExtension() => PrismContainerExtension.Current;
Everything is finally back to working!
If you want to use IServiceCollection extensions such as AddHttpClient I would suggest that you use the Prism Container Extensions. In your case it would be Prism.DryIoc.Extensions. The Container Extensions provide a lot of additional support including support for registering services with via the Service Collection extensions.
You can either install Prism.Forms.Extended and it will all just work, or you can update your App as follows:
protected override IContainerExtension CreateContainerExtension() =>
PrismContainerExtension.Current;
Adding as this is the only post I've found in weeks of searching that explains how to do this.
I'm using Unity rather than Dryloc but the solution is the same.
Install ONLY these additional packages:
Prism.Forms.Extended
Prism.Unity.Extensions
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
//Omitted Code
containerRegistry.RegisterServices(serviceCollection =>
{
serviceCollection.AddHttpClient<IApiService, ApiService>(client =>
{
client.BaseAddress = new Uri("Your Address Here");
});
});
}
public ApiService(HttpClient client)
{
//Do Stuff
}
I am trying to enable Application Insights in a console application using IHostedService (for the moment, it's a simple console application which we run as WebJob, in future in containers).
As far as my knowledge goes, in the following code, so far we do not have any extension to register globally Application Insights as an implementation of ILogger:
public static class Program
{
public static Task Main(string[] args)
{
var hostBuilder = new HostBuilder()
.ConfigureHostConfiguration(config =>
{
config.SetBasePath(Directory.GetCurrentDirectory());
config.AddJsonFile("appsettings.json", optional: false);
config.AddEnvironmentVariables();
})
.ConfigureLogging((context, logging) =>
{
logging.AddConfiguration(context.Configuration.GetSection("Logging"));
if (context.HostingEnvironment.IsDevelopment())
{
logging.AddConsole();
}
else
{
//TODO: register ApplicationInsights
}
});
return hostBuilder.RunConsoleAsync();
}
}
So far, I found out that potentially, I should be able to set everything up using custom implementation of the logger, i.e. public class ApplicationInsightsLogger : ILogger, and then... register it in the container so that DI resolves it.
Is this the right direction?
I made an extension that I could use from either an IHost or an IWebHost:
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.ApplicationInsights;
public static class LoggingBuilderExtensions
{
public static ILoggingBuilder AddLogging(this ILoggingBuilder loggingBuilder)
{
loggingBuilder.AddFilter<ApplicationInsightsLoggerProvider>("", LogLevel.Trace);
loggingBuilder.AddAzureWebAppDiagnostics();
loggingBuilder.AddApplicationInsights();
return loggingBuilder;
}
}
Since I'm not sending in the context (HostBuilderContext or WebHostBuilderContext), I can use it in either app type like this:
new HostBuilder().ConfigureLogging(loggingBuilder => loggingBuilder.AddLogging())
or
WebHost.CreateDefaultBuilder().ConfigureLogging(loggingBuilder => loggingBuilder.AddLogging())
If you needed a specific property from the context (like environment type), you could extract that and send it in as a parameter to the extension.
Here's a reference: https://github.com/Microsoft/ApplicationInsights-dotnet-logging/blob/develop/src/ILogger/Readme.md
I have a simple console application which uses Autofac as IoC container.
class Program
{
static IContainer container;
static void Main(string[] args)
{
container = Configure();
Run();
}
private static void Run()
{
using (var scope = container.BeginLifetimeScope())
{
var t = scope.Resolve<ITest1>();
var s = t.TestMethod1("");
Console.WriteLine(s);
}
}
private static IContainer Configure()
{
var builder = new ContainerBuilder();
builder.RegisterType<TestClass1>()
.As<ITest1>();
builder.RegisterType<TestClass2>()
.As<ITest2>();
return builder.Build();
}
}
The application calls the method "TestMethod1" in "TestClass1".
public interface ITest1
{
string TestMethod1(string s);
}
public class TestClass1 : ITest1
{
ITest2 f;
public TestClass1(Func<ITest2> test2Factory)
{
f = test2Factory();
}
public string TestMethod1(string s)
{
var r = string.Empty;
r = f.TestMethod2(s);
r += ":TestMethod1";
return r;
}
}
TestClass1 has a dependency on TestClass2 which declares a delegate factory for Autofac to use with the TestClass1 constructor.
public interface ITest2
{
string TestMethod2(string s);
}
public class TestClass2 : ITest2
{
public delegate TestClass2 Factory();
public virtual string TestMethod2(string s)
{
return ":TestMethod2";
}
}
This all works as expected - Autofac resolves the TestClass2 dependency, and I get the output ":TestMethod2:TestMethod1".
Now I want to mock TestClass2 using Moq and the Autofac.Extras.Moq extensions. I add the following method to the console application, and call it from the Program Main method.
private static void Test()
{
using (var mock = AutoMock.GetLoose())
{
mock.Mock<TestClass2>()
.Setup(t => t.TestMethod2(""))
.Returns(":NOT_TEST_METHOD2");
var s = mock.Create<TestClass1>();
var r = s.TestMethod1("cheese");
Console.WriteLine(r);
}
}
Now I get the output ":TestMethod1" when I expect ":NOT_TEST_METHOD2:TestMethod1". It seems the mock has not been called. This is confirmed when I step through the code.
I have also tried resolving the mock using mock.Provide(), as has been suggested elsewhere (see below). Still no luck.
var wc = Moq.Mock.Of<TestClass2>(f => f.TestMethod2("") == ":NOT_TEST_METHOD2");
Func<string, ITest2> factory = x => wc;
mock.Provide(factory);
This seems like a really simple scenario but I've not found a working answer anywhere. Can anyone see what I'm doing wrong?
Thanks for any help!
I don't use the AutoMock support, but I do know my Autofac, so I can give it a shot.
It looks like TestClass1 takes an ITest2 and not a TestClass2 in its constructor, I'm guessing if you switched to this:
mock.Mock<ITest2>()
.Setup(t => t.TestMethod2(""))
.Returns(":NOT_TEST_METHOD2");
...then it might work.
Thanks Travis. Your suggestion didn't work for me, but it made me think about the issue differently and taking a step back made me realise I was looking for a complex solution where one wasn't required. Namely, that only Moq was needed, Autofac AutoMock extensions were not. The following code worked:
private static void Test()
{
Func<ITest2> func = () =>
{
var x = new Mock<ITest2>();
x.Setup(t => t.TestMethod2("")).Returns(":NOT_TEST_METHOD2");
return x.Object;
};
var s = new TestClass1(func);
var r = s.TestMethod1("");
}
The question was answered by this post Using Moq to Mock a Func<> constructor parameter and Verify it was called twice.