I am trying to set up dependency injection in a small blazor-based web app.
My current code is:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
AddDeliveryStats(builder.Services, builder.Configuration);
// ...blazor template things...
void AddDeliveryStats(IServiceCollection services, ConfigurationManager config)
{
services.Configure<BigQuerySettings>(config.GetSection("BigQuery"));
services.AddTransient<IBigQueryClient, BigQueryClient>();
// ...other stuff not pertinent to the error...
}
where BigQuerySettings is given as
public class BigQuerySettings
{
public string ProjectId { get; set; }
public string DataSetId { get; set; }
public string AuthFilePath { get; set; }
}
and BigQueryClient has the following constructor:
public BigQueryClient(
BigQuerySettings bigQuerySettings,
ILogger<BigQueryClient> logger) { /* ... */ }
and my appsettings.json contains the following:
{
// ...
"BigQuery": {
"ProjectId": "<project-identifier>",
"DataSetId": "",
"AuthFilePath": "BigQueryAuthProd.json"
}
}
and if this looks pretty much like a tutorial example, that's because it basically is. It does not work and it is not obvious why. I get the following error:
Some services are not able to be constructed
(Error while validating the service descriptor 'ServiceType: IBigQueryClient Lifetime: Transient ImplementationType: BigQueryClient': Unable to resolve service for type 'BigQuerySettings' while attempting to activate 'BigQueryClient'.)
I have copied this code from online tutorial examples and adapted it as appropriate to my own classes, and I have read every piece of documentation I have been able to find (much of which I can't understand) and googled at least ten different permutations of the keywords in the error message. Nothing really points to what I am doing wrong.
By default, a call to services.Configure will only allow injecting an IOption<BigQuerySettings> into your consumers.
If, however, you wish to inject BigQuerySettings directly into your consumer (which I would argue you should), you should do the following:
BigQuerySettings settings =
Configuration.GetSection("BigQuery").Get<BigQuerySettings>();
// TODO: Verify settings here (if required)
// Register BigQuerySettings as singleton in the container.
services.AddSingleton<BigQuerySettings>(settings);
This allows BigQuerySettings to be injected into BigQueryClient.
Related
Someone out there must have run into this already...
I created a WebApi solution with swagger implemented, full documentation, the whole 9 yards!
When I run my web api solution, see the swagger output (and I've tested the endpoints, all working fine)
I can see the swagger definition: https://localhost:5001/swagger/v1/swagger.json
Now, I want to consume this Api as a connected service on my web app.
So following every single tutorial online:
I go to my webapp
right click on Connected Services
Add Connected Service
Add Service Reference > OpenApi > add Url, namespace & class name
That generates a partial class in my solution (MyTestApiClient)
public parial class MyTestApiClient
{
// auto generated code
}
Next step, inject the service in Startup.cs
services.AddTransient(x =>
{
var client = new MyTestApiClient("https://localhost:5001", new HttpClient());
return client;
});
Then, inject the class into some class where it's consumed and this all works
public class TestService
{
private readonly MyTestApiClient _client; // this is class, not an interface -> my problem
public TestService(MyTestApiClient client)
{
_client = client;
}
public async Task<int> GetCountAsync()
{
return _client.GetCountAsync();
}
}
So everything up to here works. BUT, this generated OpenApi client doesn't have an interface which sucks for the purposes of DI and Unit Testing.
I got around this by creating a local interface IMyTestApiClient, added to the generated class (MyTestApiClient). I only have 1 endpoint in my WebApi so have to declare that on my interface.
public parial class MyTestApiClient : IMyTestApiClient
{
// auto generated code
}
public interface IMyTestApiClient
{
// implemented in generated MyTestApiClient class
Task<int> GetCountAsync();
}
services.AddTransient<IMyTestApiClient, MyTestApiClient>(x =>
{
IMyTestApiClient client = new MyTestApiClient("https://localhost:5001", new HttpClient());
return client;
});
public class TestService
{
private readonly IMyTestApiClient _client; // now injecting local interface instead of the generated class - great success
public TestService(IMyTestApiClient client)
{
_client = client;
}
public async Task<int> GetCountAsync()
{
return _client.GetCountAsync();
}
}
But this is a bad approach because it makes me manually create an interface and explicitly declare the methods I want to consume. Furthermore, every time my Api gets updated, I will have to tweak my local interface.
So question time:
How can I add an OpenApi Service Reference that automagically also generates an interface as well?
Thanks in advance for any help getting to a viable solution.
You may have already found the answer but I had the same issue and managed to resolve it by adding /GenerateClientInterfaces:true in the Options section for the OpenAPI reference in my .csproj:
<OpenApiReference Include="api.json" CodeGenerator="NSwagCSharp" Namespace="MyNamespace" ClassName="MyClassName">
<SourceUri>https://localhost:7040/swagger/v1/swagger.json</SourceUri>
<OutputPath>MyClient.cs</OutputPath>
<Options>/GenerateClientInterfaces:true</Options>
</OpenApiReference>
I'm trying to use nancy with JSON.net, follow the 2 ways that i found to register the dependencies but all way get me to an InvalidOperationException with a message "Something went wrong when trying to satisfy one of the dependencies during composition, make sure that you've registered all new dependencies in the container and inspect the innerexception for more details." with an inner exection of {"Unable to resolve type: Nancy.NancyEngine"}.
I'm using self hosting to run nancy and jeep everything really simple to been able just to test.
public static void Main(string[] args)
{
try
{
var host = new NancyHost(new Uri("http://localhost:8888/"));
host.Start(); // start hosting
Console.ReadKey();
host.Stop(); // stop hosting
}
catch
{
throw;
}
}
First I create a customSerializer
public class CustomJsonSerializer : JsonSerializer
{
public CustomJsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver();
Formatting = Formatting.Indented;
}
}
and then i tried 2 ways of registering
Using IRegistrations:
public class JsonRegistration : IRegistrations
{
public IEnumerable<TypeRegistration> TypeRegistrations
{
get
{
yield return new TypeRegistration(typeof(JsonSerializer), typeof(CustomJsonSerializer));
}
}
public IEnumerable<CollectionTypeRegistration> CollectionTypeRegistrations { get; protected set; }
public IEnumerable<InstanceRegistration> InstanceRegistrations { get; protected set; }
}
And also using Bootstrapper
public class NancyBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container);
container.Register<JsonSerializer, CustomJsonSerializer>();
}
}
Which means that when self hosting I add the custom bootstrapper
var host = new NancyHost(new Uri("http://localhost:8888/"), new NancyBootstrapper());
Both way return the same error.
Problem is actually the versions, the nancy json.net package is using Newton.Json 6.0.0.0, BUT when installing the package it will install automatically newer version that will create this problem. Not sure what has change in the Newton.JSON that will actually create this.
https://github.com/NancyFx/Nancy.Serialization.JsonNet/issues/27
Just to add my hard won knowledge in this area, after a greatly frustrating few hours using Nancy 1.4.1.
If you use a custom bootstrapper, make sure you make the call to base.ConfigureApplicationContainer(container); before you start your custom registrations.
So, not:
public class MyCustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
// MY BITS HERE...
base.ConfigureApplicationContainer(container);
}
}
but,
public class MyCustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container); // Must go first!!
// MY BITS HERE...
}
}
If you don't do this you will get the following error:
Something went wrong when trying to satisfy one of the dependencies
during composition, make sure that you've registered all new
dependencies in the container and inspect the innerexception for more
details.
with a helpful inner exception of:
Unable to resolve type: Nancy.NancyEngine
The solution of changing the order of these C# statements was actually alluded to in #StevenRobbins' excellent answer here (which I could have saved myself several hours of pain if I'd only read properly the first time).
Says Steven:
By calling "base" after you've made a manual registration you are
effectively copying over your original registration by autoregister.
Either don't call base, or call it before you do your manual
registrations.
I haven't found a solution how to consume other interface then published.
In simple case if I want to publish IMessage and consume IMessage I have to share assembly with IMessage definition between two applications.
But what if this two applications are developing by different companies.
In this case I have two options:
make an agreement about common interfaces, naming conventions etc and share a common library
let both companies do there job as they are used to do and inside service bus (or application server) map data types.
Second option is more appropriate for me, but I haven't found a solution.
For example, I might have an employee in one system as
public interface IEmployee
{
int ID { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
}
And in other system as
public interface ILightEmployee
{
int id { get; set; }
string full_name { get; set; }
}
I want to publish IEmployee and consume ILightEmployee.
During serialization/deserialization phase in service bus I want to
use some mapping of properties and archive something like this (it is more like a pseudo code):
public class ContractMapper
{
public LightEmployee Map(IEmployee employee)
{
return new LightEmployee()
{
id = employee.ID,
full_name = employee.LastName + " " + employee.FirstName
};
}
}
For example MuleESB provides an editor for this transformations/mapping. LINK
It is unnecessary advanced solution for me, but at least in code I want do to the same thing.
Is it possible using Rebus service bus?
As long as Rebus is able to properly deserialize the incoming JSON object into a concrete class, it will attempt to dispatch the message to all polymorphically compatible handlers.
With the default Newtonsoft JSON.NET and the Jil-based JSON serializer, the rbs2-content-type header will be set to application/json;charset=utf-8, and as long as an incoming message's header starts with application/json and has a compatible encoding, both serializers will try to deserialize to the type specified by the rbs2-msg-type header.
So, if you have a matching concrete class available in the app domain, you can have a handler that implements IHandleMessages<IEmployee> or IHandleMessages<ILightEmployee> - or IHandleMessages<object> for that matter, because that handler is polymorphically compatible with all incoming messages.
The Jil serializer is special though, in that it will deserialize to a dynamic if the cannot find the .NET type that it was supposed to deserialize into.
This means that this handler:
public class HandlesEverything : IHandleMessages<object>
{
public async Task Handle(dynamic message)
{
// .... woohoo!
}
}
coupled with the Jil serializer will be able to handle all messages, dynamically, picking out whichever pieces it is interested in.
I hope the answer gives an impression of some of the possibilities with Rebus. Please tell me more if there's a scenario that you feel is not somehow covered well.
We are using classes inheriting from Registry to configure our StructureMap container in our ASP.NET MVC 4 application startup.
Some excerpt from one of the registry-classes:
For<ISomeInterface>().HybridHttpOrThreadLocalScoped().Use<SomeImplementation>();
We would like use different instances of our interfaces depending on the context. (For example switching from database "online" mode to "maintenance" mode where everything is saved on filesystem; therefore using other interfaces (i.e. repositories) all over the place in our application)
For example by default it should use SomeImplementation but when passing some kind of querystring in the url (to name a simple "context" scenario) it should use SomeOtherImplementation.
How can this be achieved for multiple interfaces/types?
Should we use named instances for this? Like:
For<ISomeInterface>().HybridHttpOrThreadLocalScoped().Use<SomeOtherImplementation>().Named("other");
I read about StructureMap Profiles but i'm not sure if this is the right way to go.
Should we use profiles for this? Like i.e.:
Profile("other", profileExpression =>
{
For<ISomeInterface>().HybridHttpOrThreadLocalScoped().Use<SomeOtherImplementation>();
});
How can we switch different configurations on the fly?
ObjectFactory.Container.SetDefaultsToProfile("other");
This way? (At what stage in mvc "life-cycle" this can happen at the earliest?)
Can this be a temporary switch for just the current request or current users session?
Thanks in advance!
From my experience, runtime configuration like this is best achieved using an abstract factory that is responsible for creating your dependency during runtime.
This dependency can then be registered with StructureMap like so:
Your registry:
public class StorageRegistry : Registry
{
public StorageRegistry()
{
...
this.For<IDataStoreInstance>().Use(ctx => ctx.GetInstance<DataStoreAbstractFactory>().ConfigureStorage());
...
}
}
Now your DataStoreAbstractFactory is responsible for creating and configure the necessary storage instance based on your configuration. As DataStoreAbstractFactory is now registered with StructureMap you're able to inject the necessary dependencies into it for determining which storage method to use.
Implementation example:
public class DataStoreAbstractFactory
{
public DataStoreAbstractFactory()
{
// Dependencies to figure out data storage method can be injected here.
}
public IDataStoreInstance ConfigureStorage()
{
// This method can be used to return type of storage based on your configuration (ie: online or maintenance)
}
}
public interface IDataStoreInstance
{
void Save();
}
public class DatabaseStorage : IDataStoreInstance
{
public void Save()
{
// Implementation details of persisting data in a database
}
}
public class FileStorage : IDataStoreInstance
{
public void Save()
{
// Implementation details of persisting data in a file system
}
}
Usage:
Your controller/services or whatever are now completely unaware of what storage method they're using when accessing and persisting data.
public class UpdateController : Controller
{
public IDataStoreInstance StorageInstance { get; set; }
public UpdateController(IDataStoreInstance storageInstance)
{
StorageInstance = storageInstance;
}
[HttpPost]
public ActionResult Index()
{
...
this.StorageInstance.Save();
...
}
...
}
I have an ASP.NET Web Forms application with UI, Service layer and Repository layer.
Some of the methods in my Service Layer communicates with a Web Service, therefore I would like to wrap all the calls to the Web Methods in a Try-Catch-Finally construct.
Suppose I have the following methods in my Service Layer:
public RegistrationDetails GetRegistrationDetails(int userId)
public bool RegisterUser(UserData myUserData)
Where RegistrationDetails and myUserData are object types (classes).
My concern is the following: if I create a Try-Catch-Finally to wrap the call to the Web Service within the implementation of the methods listed above, in case there is an exception how can I return the message string if the return types are RegistrationDetails and bool?
I was thinking about adding a property to every return object but I do not know if that is a good solution. For instance instead of using bool:
public class RegisterResponse
{
public bool isRegistered { get; set; }
public string ExceptionMessage { get; set; }
}
public RegisterResponse RegisterUser(UserData myUserData)
And then check if ExceptionMessage is null or String.Empty. Is it a good approach? Thanks
1) As mentioned by IrishChieftain, bubbling the exception down to the forms is good, you will be able to respond to the exception better
2) You can also have a reference parameter as array which stores exception messages generated from the method
public bool RegisterUser(UserData myUserData, optional ref ArrayList<string> errors)
{
if(error)
errors.Add("This error occured")
}
3) For Instance Object, you could have an Instance variable of ArrayList for errors and have that returned in a property
public class MyClass
{
private ArrayList<string> errors = new ArrayList<string>
public ArrayList<string> ExceptionMessages()
{
get
{
return errors;
}
}
public RegistrationDetails GetRegistrationDetails(int userId) { }
}
//Used like this:
MyClass c = new MyClass();
c.GetRegistrationDetails();
if(c.ExceptionMessages > 0)
{
//output errors
}
But I would prefer the first approach - for flexibility like output formatting
Passing raw exceptions on to your client (web forms layer) from a service could be risky. If it's a database exception, it might expose details of your database. A malicious user might call your service layer methods from their own application.
You can expect two types of exceptions on the client level:
Communication Exception (problems connecting to the service)
Server-side error (database problem, username not unique, password invalid... other business rule exceptions)
The first type should be caught in try-catch-finally in your web forms layer, but the second kind should be caught in the service layer, logged, then wrapped up in the RegisterResponse object, as you suggest. But, instead of sending Exception.Message, you might consider using an enum of expected errors (with a ServerError member to cover anything else.) You could also add an EventId to the response and log entry so that you can investigate errors.
public enum RegisterResponseError { NoError = 0, SystemError = 1,
UserNameNotUnique, PasswordInvalid, etc. }
public class RegisterResponse
{
public bool isRegistered { get; set; }
public RegisterResponseError ErrorCode { get; set; }
}
Then in your client code,
if(myRegisterResponse.ErrorCode == RegisterResponseError.NoError)
// everything was fine
else
// show a suitable error message for ErrorCode and display EventId (if logging)
You could return an error string from your service, but it's probably better to manage any content in your web forms layer, in case you need to localize the content or use a CMS later on.