In a .NET Framework project without dependency injection, is it better to get an HttpClient instance through its constructor (new HttpClient) or through IHttpClientFactory? Which of the following two cases is better?
HttpClient constructor
Public Shared Function GetHttpClient() As HttpClient
Return new HttpClient
End Function
IHttpClientFactory
Private Shared mHttpClientFactory As IHttpClientFactory
Shared Sub New()
Dim serviceProvider = New ServiceCollection().AddHttpClient().BuildServiceProvider()
mHttpClientFactory = serviceProvider.GetService(Of IHttpClientFactory)
End Sub
Public Shared Function GetHttpClient() As HttpClient
Return mHttpClientFactory.CreateClient
End Function
As I have read, it is recommended to reuse the HttpClient instance created in case 1.
Shall I reuse the HttpClient instance created in case 2, or may I repeatedly call GetHttpClient, without reusing the instance returned?
Related
(I am writing a processor that handles requests in a queue (console app).
I would like to use the .NET Core DI.
So far my code looks like this:
...
var connectionString = exportConfiguration.ConnectionString;
using (var scope = _serviceProvider.CreateScope())
{
var provider = scope.ServiceProvider;
var service = provider.GetRequiredService<MyContext>();
service.SqlConnectionString = sqlConnectionString; // I don't think property injection on a dbcontext will work, it takes its connection string in via the constructor
}
I have read how to assign parameters to the object as shown above, but how do I create a new context based on the connection string that is used in all the objects that the service uses (using constructor injection because thats why dbcontexts take - connection string in constructor)?
(I am not storing my connection string in the queue by the way, a code comes down the queue and my app then chooses the connection string to use).
I have managed to work this out. The key was that when you use CreateScope(), then GetRequiredService(), the DI system will provide new objects. So I just had to provide the correct information. This is now what my code looks like:
// Prior code gets information from a queue, which could be different every time.
// This needs passing as a constructor to the DbContext and possibly other information from the queue to other methods constructors
// (constructor injection not property injection)
var connectionString = queueItem.ConnectionString;
// save the connection string so the DI system (startup.cs) can pick it up
Startup.ConnectionString = connectionString;
using (var scope = _serviceProvider.CreateScope())
{
var provider = scope.ServiceProvider;
var service = provider.GetRequiredService<IMyService>();
// go off and get data from the correct dbcontext / connection string
var data = service.GetData();
// more processing
}
/// The Service has the DbContext in its constructor:
public class MyService : IMyService {
private DbContext _dbContext;
public MyService(DbContext dbContext) {
_dbContext = dbContext;
}
// more stuff that uses dbcontext
}
/// In startup.cs:
public static string ConnectionString {get;set;}
...
builder.Services.AddScoped<IMyService, MyService>();
builder.Services.AddScoped<DbContext>(options => options.UseSqlServer(Startup.ConnectionString));
// Also the following code will work if needed:
// Parameter1 is something that comes from the queue and could be different for each
// CreateScope()
build.Services.AddScoped<IMyOtherService>((_) =>
new MyOtherService(Startup.Parameter1));
I hope this helps somebody, because when I was googling around I couldn't find out how to do this.
I'm trying to explicitly create a db context in .NET Core 3 startup
I know I can do this in startup.cs ConfigureServices to inject a dbcontext into the controller (which works fine):
String dbconn = Configuration["ConnectionStrings:VerseDBConnectionStringMSSQL"];
services.AddDbContext<VerseDBContext>(options => options.UseSqlServer(dbconn));
but I am trying to generalize the storage provider (and keep the controller code the same for all storage readers), so it takes an IVerseStorageReader interface, instead of a DB context (as I may want to read from memory, or xmlfile, etc) and use the same code in the controller, just switch it based on config in appsettings. One of the VerseStorageReaders takes a db context in constructor:
public class DBVerseReader : IVerseStorageReader
{
private VerseDBContext _dbContext;
public DBVerseReader(VerseDBContext dbContext)
{
_dbContext = dbContext;
}
...
}
My problem is: I can't quite figure out the syntax right for creating the db context explicitly. I'm very close (I think) but this doesn't work:
String dbconn = Configuration["ConnectionStrings:VerseDBConnectionStringMySQL"];
var optionsBuilder = new DbContextOptionsBuilder<VerseDBContext>();
optionsBuilder.UseMySql(dbconn);
VerseDBContext x = optionsBuilder.UseMySql<VerseDBContext>(dbconn); <-- compile error
services.AddSingleton<IVerseStorageReader>(new DBVerseReader(x));
Can someone clue me on what I'm doing wrong? What I'm trying to inject is an instance of IVerseStorageReader, not a DBContext. There are overloads of VerseStorageReader that take a db context as input, and others which take other inputs (e.g. xmlfilename, etc)...so I want startup to add an instance of one of the IVerseStorageReaders and that gets injected (not a dbcontext injection).
You have to get the options from the builder after configuring it
String dbconn = Configuration["ConnectionStrings:VerseDBConnectionStringMySQL"];
var optionsBuilder = new DbContextOptionsBuilder<VerseDBContext>();
optionsBuilder.UseMySql(dbconn);
DbContextOptions<VerseDBContext> options = optionsBuilder.Options;
VerseDBContext x = new VerseDBContext(options);
services.AddSingleton<IVerseStorageReader>(new DBVerseReader(x));
But since DbContext derived classes are usually registered as scoped, I would suggest you move the context into the factory delegate and register the service abstraction as scoped also.
String dbconn = Configuration["ConnectionStrings:VerseDBConnectionStringMySQL"];
var optionsBuilder = new DbContextOptionsBuilder<VerseDBContext>();
optionsBuilder.UseMySql(dbconn);
DbContextOptions<VerseDBContext> options = optionsBuilder.Options;
services.AddScoped<IVerseStorageReader>( sp => {
VerseDBContext x = new VerseDBContext(options);
return new DBVerseReader(x);
});
I write some tests of created system which worked with PostgreSQL. I create in solution new project with type Class Library (.NET Core). Then, i create class, which testing class DocumentRepository. But in constructor of DocumentRepository is used IConfiguration (for connecting with database), and this IConfiguration i can't call in test class. How i can to imitate connecting with database in UnitTest?
Here class, which i want testing
public class DocumentsRepository : IRepository<Documents>
{
private string connectionString;
public DocumentsRepository(IConfiguration configuration, string login, string password)
{
connectionString = configuration.GetValue<string>("DBInfo:ConnectionString");
connectionString = connectionString.Replace("username", login);
connectionString = connectionString.Replace("userpassword", password);
}
internal IDbConnection Connection
{
get
{
return new NpgsqlConnection(connectionString);
}
}
public void Add(Documents item)
{
using (IDbConnection dbConnection = Connection)
{
dbConnection.Open();
dbConnection.Execute("SELECT addrecuserdocuments(#DocumentName,#Contents,#DocumentIntroNumber)", item);
}
}
Here's test, which i try use
using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WebApplication4.Controllers;
using WebApplication4.Entites;
using WebApplication4.ViewModels;
using Xunit;
namespace TestsApp
{
public class UserControllerTest
{
private IConfiguration configuration;
private string connectionString;
[Fact]
public async Task IndexUsers()
{
connectionString = configuration.GetValue<string>("DBInfo:ConnectionString");
var aCon = new AccountController(configuration);
var uCon = new UserController(configuration);
LoginModel model = new LoginModel
{
Login = "postgres",
Password = "111"
};
aCon.Authorization(model);
var result = uCon.Index();
var okResult = result.Should().BeOfType<OkObjectResult>().Subject;
var persons = okResult.Value.Should().BeAssignableTo<IEnumerable<Documents>>().Subject;
persons.Count().Should().Be(7);
}
}
}
Test show my error on
var result = uCon.Index();
And get me NullReferenceException.
How i can resolve this problem?
First and foremost, you're not unit testing, you're integration testing. As soon as you've got something like a database connection in the mix, unit testing is well out the window. If your goal is to write unit tests for your repository class, you should be mocking the data store.
Second, you should not inject IConfiguration, if you need some data from your configuration, such as a connection string, you should bind it to a strongly-typed class, and inject that instead:
services.Configure<MyConnectionStringsClass>(Configuration.GetSection("ConnectionStrings"));
Then, inject IOptionsSnapshot<MyConnectionStringsClass> instead.
Third, you really shouldn't be handling it this way, anyways. If you repository has a dependency on IDbConnection, then you should be injecting that into your repository. In Startup.cs:
services.AddScoped(p => new NpgsqlConnection(Configuration.GetConnectionString("Foo"));
Then, accept NpgsqlConnection in your repo constructor and set it to a private field.
Fourth, if you insist on continuing the way you currently are, you should absolutely not have a custom getter on your Connection property that news up NpgsqlConnection. That means you'll get a new instance every single time you access this property. Instead, you should define it as simple { get; private set; }, and set it in your repo's constructor.
Fifth, you should not be using using with a property defined in either way, as it will be disposed after the first time you do it, making all subsequent queries fail with an ObjectDisposedException. If you're going to new it up in your class, then your class needs to implement IDisposable and you should dispose of your connection in the Dispose method. FWIW, if you inject all dependencies (including your connection) into your class, you don't need to implement IDisposable as there's nothing the class will own that it needs to dispose of - another great reason to use dependency injection all the way down.
Finally, to answer you main question, you should use TestServer. When creating a TestServer you pass it your Startup class as a type param, so you end up with a true replica of your actual app, with all the appropriate services and such. Then, you can issue HTTP requests, like you would with HttpClient to test your controller actions and such. However, again, this is for integration testing only, which is the only time you should actually have a PostreSQL database in-play anyways.
I have a web application. I found that performance bottleneck could be that i am creating Http client again and again for every request.
public static class DemoHttpClient
{
public static HttpClient GetClient()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(DemoConstants.DemoAPI);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
}
public class DemoConstants
{
public const string DemoAPI = "http://localhost/";
}
I am planning to implement singleton for this. And found this very helpful article.
http://csharpindepth.com/Articles/General/Singleton.aspx
I am confused as to how exactly ASP.NET MVC web application lifecycle is with when it is deployed on the server. Assuming there will be multiple threads calling same resource, the resource further again and again making new http clients..
What should we do here..
1) Lazily load HTTP client?
2) Not lazily load it?
Which particular approach should we use?
This doesn't sound like a good idea. In particular, take a peek into the docs of the HttpClient class:
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
https://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.118%29.aspx
This means that accessing the very same singleton instance from multiple threads will lead to undefined issues.
What you could do however, is you could reuse the same instance across a single request. This can be done by storing an instance in the Items container:
private static string ITEMSKEY = "____hclient";
public static HttpClient GetClient()
{
if ( HttpContext.Current.Items[ITEMSKEY] == null )
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(DemoConstants.DemoAPI);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpContext.Current.Items.Add( ITEMSKEY, client );
}
return (HttpClient)HttpContext.Current.Items[ITEMSKEY];
}
Note, that since the HttpClient implements IDisposable, it still could be a good idea to dispose such instance somewhere in the pipeline, for example in the EndRequest event of the application pipeline.
Update: as noted in a comment by #LukeH, the updated version of the docs for the .NET 4.5 and 4.6 states that some of methods of the HttpClient class are thread safe:
https://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.110%29.aspx
The updated remarks section states that a single instance is basically a collection of shared settings applied to all requests executed by this instance. Then, the docs says:
In addition, every HttpClient instance uses its own connection pool, isolating its requests from requests executed by other HttpClient instances.
This means that the isolation of different pools could still make sense, my personal recommendation would still be then to not to have a singleton, as you possibly would still need to change some settings between consecutive requests.
In my ASP.NET Web Service I've included a module in which their are Public declared variables. Will they be thread-safe? Will they get mixed up upon simultaneous calls?
Those variables are mostly DatsSet, DataTable and SQLDataAdapter..
Partial code of the module:
Imports System.Data.OleDb
Imports System.Diagnostics
Module modCommon
Public bDoLog As Boolean
Public sCurrentODBC As String
Public cn As SqlConnection
Public Query1ds As DataSet
Public Query1 As DataTable
Public Query1adapter As SqlDataAdapter
#scripni
Thanks, as I'm not familiary with your suggestions, I will move everything locally.
Additionally, will the following variables be thread-safe?:
[ToolboxItem(False)]_
Public Class Service1
Inherits System.Web.Services.WebService
Dim sName As String
Dim sCurrentPath As String
[WebMethod()]_
Public Function Capture(ByVal sPath As String) As String
sName = "Joe"
End Function
End Class
If you're using web services than yes, you will have concurency problems when multiple services will try to access the same resource, and SqlConnection is definetly a resource you don't want shared.
You should make sure that you don't have simultaneous calls to the properties (for ex. by wrapping the fields with getters / setters and implementing locks in those methods) or by moving the code to a class and instantiating that class whenever you need it.