I want to do something like this Autofac line below with DryIoC
builder.RegisterType<TenantDBContext>
().InstancePerLifetimeScope().WithParameter(new
NamedParameter("connectionString", ""));
I have a CoreDBContext that has the connection string of TenantDBContext. Is it valid to pass the connection string at the point of registering my context in DryIoc?
container.Register<DbContext>(
Reuse.InCurrentScope,
made: Parameters.Of.Name("connectionString", _ => ""));
Update:
Asuming that you want to automatically select from multiple constructors:
Here is live snippet
container.Register<DbContext>(
Reuse.InCurrentScope,
made: Made.Of(FactoryMethod.ConstructorWithResolvableArguments,
Parameters.Of.Name("connectionString", _ => "")));
Note, that version above is brittle to parameter name change, so you may consider strongly-typed constructor expression:
container.Register<DbContext>(
Made.Of(() => new DbContext("")),
reuse: Reuse.InCurrentScope);
Related
With reference to .Net Maui: How to read/write (get/set) a global object from any content page (MVVM)
I now have a need to pass an object that is rather a large (>500Mb), it is an OpenMap extract, a RouterDb object (Itinero http://docs.itinero.tech/index.html)
Though I am trying to use MVVM some packages (Mapsui) use code behind so I am left with a mixture of ViewModel and Codebehind. I can overcome the limitation of not having any kind of global object reference by using the MessagingCenter which works remarkably well.
I am wondering if this mechanism can handle the passing of large objects (megabytes) or if internally it simply passes a reference to the object. I suspect from my experiments that it is trying to pass a copy of the object and failing, as I end up with a null object.
Here's the code
MessagingMarker.cs - just a placeholder class
namespace RouteIt.Models;
public class MessagingMarker
{
}
In my MainPageViewModel.cs
//Takes about 10seconds to load
using (var stream = new FileInfo(#"C:\Users\Gordon\source\repos\RouteIt\Resources\Raw\gb.routerdb").OpenRead())
{
routerDb = RouterDb.Deserialize(stream);
}
//send message to codebehind containing object or reference?
MessagingCenter.Send(new MessagingMarker(), "RouterDbLoaded", routerDb);
and in my receiving MainPage.xaml.cs constructor
MessagingCenter.Subscribe<MessagingMarker, RouterDb>(this, "RouterDbLoaded", (sender, arg) =>
{
routerDb = arg;
});
router = new(routerDb);
Any thoughts? (And yes perhaps I need to re-struture my app, but at this stage I don't want to really, one day Mapsui might support mvvm apparently.)
As always thanks for at least reading :)
I'm using Autofac for DI with the builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest(); but sometimes it gives an error on all my controllers (System.InvalidOperationException: An error occurred when trying to create a controller of type 'UserController'. Make sure that the controller has a parameterless public constructor. ---> Autofac.Core.DependencyResolutionException)
and when I give a rebuild of WEB API and it starts working fine.
Here is my code in startup.cs
private void ConfigureAutofac(HttpConfiguration config, IAppBuilder app)
{
var builder = new ContainerBuilder();
//Register HttpRequestMessage
builder.RegisterType<CurrentRequest>().InstancePerRequest();
builder.Register(c => new UrlHelper(c.Resolve<CurrentRequest>().Value));
//Register Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
//Register the Autofac filter provider.
builder.RegisterWebApiFilterProvider(config);
//Register the Autofac model binder provider.
builder.RegisterWebApiModelBinderProvider();
#region Register managers
var businessasm = BuildManager.GetReferencedAssemblies()
.Cast<Assembly>()
.Where(n => n.FullName.Contains("Business"))
.FirstOrDefault();
builder.RegisterAssemblyTypes(businessasm)
.Where(t => t.Name.EndsWith("Manager") && t.Name != "DocumentManager")
.AsImplementedInterfaces()
.InstancePerRequest();
builder.RegisterGeneric(typeof(DocumentManager<>))
.As(typeof(IDocumentManager<>))
.InstancePerRequest();
#endregion
//Set the dependency resolver to be Autofac.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
config.MessageHandlers.Insert(0, new ApiDelegatingHandler());
config.MessageHandlers.Insert(1, new ActivityLogHandler());
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
}
The error indicates that UserController (which you didn't show above) takes a parameter that isn't registered with Autofac.
Since it works sometimes and not others, my guess is that the problem is this line:
var businessasm = BuildManager.GetReferencedAssemblies()
.Cast<Assembly>()
.Where(n => n.FullName.Contains("Business"))
.FirstOrDefault();
FirstOrDefault will return null if no assembly is found; and later on when you register all the things that end with Manager it means nothing will get registered.
My guess is that the UserController needs one of these managers, Autofac doesn't have it registered, and instantiation fails. If you have a breakpoint on the next line, you can see if businessasm is null; or, alternatively, switch to .First() instead of .FirstOrDefault() if you always expect the registrations to work.
Oh, and if you happen to have two assemblies with Business in them, you might want to make sure you're getting the right one. Assembly load order isn't guaranteed to be consistent.
That said I see the exception message you posted seems to indicate there are some nested exceptions in the stack. Don't stop reading at the first exception. Unfortunately with DI and other layers in the stack, you get exceptions that have inner exceptions that may, themselves, have inner exceptions... and the full set of messages may actually have more information that can help you troubleshoot.
Autofac has a really good troubleshooting page and Autofac v6 has some pretty detailed diagnostics that might help figure out where the missing things are if you can't figure it out from the exceptions.
I am trying to mock a suggest response, however suggestionOption.Setup(x => x.Text).Returns("Hello") is throwing an exception:
An exception of type System.NotSupportedException occurred in
Moq.dll but was not handled in user code Additional information:
Invalid setup on a non-virtual (overridable in VB) member: x => x.Text
var searchSuggestResponseMock = new Mock<ISuggestResponse>();
var suggestionOption = new Mock<SuggestOption>();
suggestionOption.Setup(x => x.Text).Returns("Hello");
suggestionOption.Setup(x => x.Payload).Returns("{path:\"drugs/hello\"}");
var suggestion = new Mock<Suggest>();
suggestion.Setup(x => x.Options).Returns(new List<SuggestOption> { suggestionOption.Object });
searchSuggestResponseMock.Setup(x => x.Suggestions).Returns(new Dictionary<string, Suggest[]>()
{
{"suggest", new Suggest[] {suggestion.Object}},
});
var mock = new Mock<IConnector>();
mock.Setup(x => x.getClient()
.Suggest<Term>(Moq.It.IsAny<Func<SuggestDescriptor<Term>,
SuggestDescriptor<Term>>>())).Returns(searchSuggestResponseMock.Object);
_connector = mock.Object;
You can't mock non-virtual methods. As the error states:
Invalid setup on non-virtual member
Moq does its magic by acting as a proxy between your code and the real class. It does this by taking advantage of virtual methods. Without having a virtual method, Moq can't intercept the call.
Neither SuggestionOption, or Suggest are easily mockable, as they have non-virtual, internal set based properties, and do not implement any specific interface.
It looks like you are maybe mocking at too low a level. If you don't want to call Elastic to get your list of suggestions then have a method which just returns an array of strings (or your own custom Suggestion class) and mock that instead.
Or just call Elastic for real, as long as you are passing in sensible values which don't return thousands of suggestions.
(Or you could in theory create instances of Suggest, and set the internal properties via reflection, but this is not ideal obviously).
I have a code in Web Api Delegating Handler that extract data from request header.
However, I can't register instance in Autofac container because Autofac container require SingleInstance only.
public class ExtractUserNameMessageHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var userNameFromFrontEnd = request.GetDependencyScope().GetService(typeof (IUserNameFromFrontEnd));
if (userNameFromFrontEnd == null)
{
var updatedContainerBuilder = new ContainerBuilder();
userNameFromFrontEnd = ExtractUserName(request);
if (userNameFromFrontEnd == null)
{
throw new Exception("We've got a request without UserName header");
}
updatedContainerBuilder.RegisterInstance(userNameFromFrontEnd)
.As<IUserNameFromFrontEnd>()
.InstancePerRequest();
var autofacDependencyResolver = GlobalConfiguration.Configuration.DependencyResolver as AutofacWebApiDependencyResolver;
if (autofacDependencyResolver == null)
{
throw new Exception("We can work with Autofac DI container");
}
updatedContainerBuilder.Update(autofacDependencyResolver.Container as IContainer);
}
When I try to update container I get an exception with message - registration can support singleinstance() sharing only.
What does it mean? I can't understand why we have this limitation. But in any cases my first goal - update container with new dependency.
Does anybody have ideas?
(Note: This question was cross-posted to the Autofac forums as well.)
When you register a specific instance, it's effectively a singleton - it's one instance, the instance you provided.
When you try to assign it InstancePerRequest or, really, any other lifetime scope besides SingleInstance, it doesn't make logical sense because you're not going to get a different instance per request (or whatever). You're going to get the exact same instance you registered, which is a singleton.
The exception message is trying to tell you how to avoid incorrect expectations: that it can't provide you a different instance per request even though you told it to because you didn't tell it how to create a new instance, you instead provided a specific instance.
If you need a different instance of an object per lifetime scope/request/whatever, you need to register a type, a delegate, or something else that tells Autofac how to create that new instance.
What that means is that if you want a different IUserNameFromFrontEnd per request, you need to move that logic out of a DelegatingHandler and into an Autofac registration delegate.
// Make sure to register the HttpRequestMessage in the container
// so you can resolve it...
builder.RegisterHttpRequestMessage(httpConfiguration);
// Then, whilst building your root container...
builder
.Register(ctx =>
{
var request = ctx.Resolve<HttpRequestMessage>();
return ExtractUserName(request);
})
.As<IUserNameFromFrontEnd>()
.InstancePerRequest();
Now it will probably do what you're looking to do - because you told Autofac how to create the instance that belongs in each request. It also means you don't need that DelegatingHandler anymore because Autofac will just do the right thing.
More advanced (and probably not useful here, but for completeness):
If, for whatever reason, you still feel like you need to modify the registration directly in the lifetime scope, instead of updating the container you should add the registration when the request lifetime scope is created.
Again, do not update the root container for per-lifetime-scope or per-request dependencies. It's not going to work how you think.
When a new lifetime scope is created, you can add registrations on the fly.
using(var scope = container.BeginLifetimeScope(
builder => builder.RegisterInstance(myfoo).As<IFoo>()))
{
// This will use the registrations in the container
// and the scope. f == myfoo
var f = scope.Resolve<IFoo>();
}
The AutofacDependencyResolver is the thing that creates the request lifetime scope and hands it off to Web API. You can see the full source here. The key method is BeginScope:
public IDependencyScope BeginScope()
{
var lifetimeScope = _container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
return new AutofacWebApiDependencyScope(lifetimeScope);
}
If you create your own AutofacDependencyResolver you can modify how the scope is created:
public IDependencyScope BeginScope()
{
var lifetimeScope = _container.BeginLifetimeScope(
MatchingScopeLifetimeTags.RequestLifetimeScopeTag,
builder => builder.RegisterInstance(myfoo).As<IFoo>());
return new AutofacWebApiDependencyScope(lifetimeScope);
}
This isn't an explicitly supported extension point in the Autofac Web API integration right now - that's why you'd have to create your own resolver.
However, this seems like overkill to solve the thing it appears you're trying to solve. I strongly recommend just registering the delegate with Autofac rather than trying to update existing containers or scopes. You will have far more luck using the path of least resistance.
I am some need help understanding the latest recommended approach to wire up and use reactiveui for a WPF project.
In doing research on the internet on reactiveui I came across various (few) posts spanning a long time period during which the library evolved with the unfortunate result that some of these how-to articles now refer to older ways of doing things which are no longer applicable
I am trying to understand the recommended way to wire up commands (usually to invoke web service which returns a DTO) and I’ve found multiple ways mentioned to do it.
My current understanding is that
// this is the first thing to do
MyCommand = ReactiveCommand.Create()
// variations to wire up the delegates / tasks to be invoked
MyCommand.CreateAsyncTask()
MyCommand.CreateAsyncFunc()
MyCommand.CreateAsyncAction()
// this seems to be only way to wire handler for receiving result
MyCommand.Subscribe
// not sure if these below are obsolete?
MyCommand.ExecuteAsync
MyCommand.RegisterAsyncTask()
Could someone try to explain which of these variations is the latest API and which are obsolete, with perhaps a few words about when to use each of them
The changes on the ReactiveCommand API are documented in this blog post:
http://log.paulbetts.org/whats-new-in-reactiveui-6-reactivecommandt/
The first option - ReactiveCommand.Create() - just creates a reactive command.
To define a command which asynchronously returns data from a service you would use :
MyCommand = ReactiveCommand.CreateAsyncTask(
canExec, // optional
async _ => await api.LoadSomeData(...));
You may use the Subscribe method to handle data when it is received:
this.Data = new ReactiveList<SomeDTO>();
MyCommand.Subscribe(items =>
{
this.Data.Clear();
foreach (var item in items)
this.Data.Add(item);
}
Though, the simplest thing is to use instead the ToProperty method like this:
this._data = MyCommand
.Select(items => new ReactiveList<SomeDTO>(items))
.ToProperty(this, x => x.Data);
where you have defined an output property for Data:
private readonly ObservableAsPropertyHelper<ReactiveList<SomeDTO>> _data;
public ReactiveList<SomeDTO> Data
{
get { return _data.Value; }
}