How to resolve with a default class if a matching register is not found in Autofac - autofac

I am using the following code for register:
builder.RegisterType<QCatVCardParser>().Named<IQCatParser>(".VCF");
builder.RegisterType<QCatVCardParser>().Named<IQCatParser>(".VCARD");
builder.RegisterType<QCatOutlookMessageParser>().Named<IQCatParser>(".MSG");
builder.RegisterType<QCatMimeMessageParser>().Named<IQCatParser>(".EML");
builder.RegisterType<QCatCalendarParser>().Named<IQCatParser>(".ICS");
container = builder.Build();
To retrieve a class reference I am using the below code:
var r = container
.ResolveNamed<IQCatParser>(Path.GetExtension(fileName).ToUpperInvariant());
Now my question is if a unsuppoted file extension comes let's say .DOC I want to resolve it with a component among one of the registered classes or else with the first registered class.
Is it possible to do this with AutoFac?

You can do this with Autofac. You need to create an IRegistrationSource that can provide the default registration if needed. For example, here is the RegistrationsFor code of a source that provides settings classes:
var typedService = service as IServiceWithType;
if (typedService != null && typedService.ServiceType.IsClass && typeof(ISettings).IsAssignableFrom(typedService.ServiceType))
{
yield return RegistrationBuilder.ForDelegate((c, p) => c.Resolve<ISettingsReader>().Load(typedService.ServiceType))
.As(typedService.ServiceType)
.CreateRegistration();
}
Then, inside of a module or directly using your Autofac builder, call RegisterSource to hook up the new registration source.

Related

Debugging Code Called by EF Core Add-Migrations

I have an Entity Framework Core database defined in a separate assembly, using the IDesignTimeDbContextFactory<> pattern (i.e., I define a class, derived from IDesignTimeDbContextFactory, which has a method called CreateDbContext that returns an instance of the database context).
Because the application of which the EF Core database is a part utilizes AutoFac dependency injection, the IDesignTimeDbContextFactory<> factory class creates an AutoFac container in its constructor, and then resolves the DbContextOptionsBuilder<>-derived class which is fed into the constructor for the database context (I do this so I can control whether a local or an Azure-based SqlServer database is targeted, based on a config file setting, with passwords stored in an Azure KeyVault):
public class TemporaryDbContextFactory : IDesignTimeDbContextFactory<FitchTrustContext>
{
private readonly FitchTrustDBOptions _dbOptions;
public TemporaryDbContextFactory()
{
// OMG, I would >>never<< have thought to do this to eliminate the default logging by this
// deeply-buried package. Thanx to Bruce Chen via
// https://stackoverflow.com/questions/47982194/suppressing-console-logging-by-azure-keyvault/48016958#48016958
LoggerCallbackHandler.UseDefaultLogging = false;
var builder = new ContainerBuilder();
builder.RegisterModule<SerilogModule>();
builder.RegisterModule<KeyVaultModule>();
builder.RegisterModule<ConfigurationModule>();
builder.RegisterModule<FitchTrustDbModule>();
var container = builder.Build();
_dbOptions = container.Resolve<FitchTrustDBOptions>() ??
throw new NullReferenceException(
$"Could not resolve {typeof(FitchTrustDBOptions).Name}");
}
public FitchTrustContext CreateDbContext( string[] args )
{
return new FitchTrustContext( _dbOptions );
}
}
public class FitchTrustDBOptions : DbContextOptionsBuilder<FitchTrustContext>
{
public FitchTrustDBOptions(IFitchTrustNGConfigurationFactory configFactory, IKeyVaultManager kvMgr)
{
if (configFactory == null)
throw new NullReferenceException(nameof(configFactory));
if (kvMgr == null)
throw new NullReferenceException(nameof(kvMgr));
var scannerConfig = configFactory.GetFromDisk()
?? throw new NullReferenceException(
"Could not retrieve ScannerConfiguration from disk");
var dbConnection = scannerConfig.Database.Connections
.SingleOrDefault(c =>
c.Location.Equals(scannerConfig.Database.Location,
StringComparison.OrdinalIgnoreCase))
?? throw new ArgumentOutOfRangeException(
$"Cannot find database connection information for location '{scannerConfig.Database.Location}'");
var temp = kvMgr.GetSecret($"DatabaseCredentials--{dbConnection.Location}--Password");
var connString = String.IsNullOrEmpty(dbConnection.UserID) || String.IsNullOrEmpty(temp)
? dbConnection.ConnectionString
: $"{dbConnection.ConnectionString}; User ID={dbConnection.UserID}; Password={temp}";
this.UseSqlServer(connString,
optionsBuilder =>
optionsBuilder.MigrationsAssembly(typeof(FitchTrustContext).GetTypeInfo().Assembly.GetName()
.Name));
}
}
Needless to say, while this provides me with a lot of flexibility (I can switch from local to cloud database just by changing a single config parameter, and any required passwords are reasonably securely stored in the cloud), it can trip up the add-migration commandlet if there's a bug in the code (e.g., the wrong name of a configuration file).
To debug those kinds of problems, I've often had to resort to outputting messages to the Visual Studio output window via diagnostic WriteLine calls. That strikes me as pretty primitive (not to mention time-consuming).
Is there a way to attach a debugger to my code that's called by add-migration so I can step thru it, check values, etc? I tried inserting a Launch() debugger line in my code, but it doesn't work. It seems to throw me into add-manager codebase, for which I have no symbols loaded, and breakpoints that I try to set in my code show up as the empty red circle: they'll never be hit.
Thoughts and suggestions would be most welcome!
Add Debugger.Launch() to the beginning of the constructor to launch the just-in-time debugger. This lets you attach VS to the process and debug it like normal.

Asp.Net Web API Error: The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'

Simplest example of this, I get a collection and try to output it via Web API:
// GET api/items
public IEnumerable<Item> Get()
{
return MyContext.Items.ToList();
}
And I get the error:
Object of type
'System.Data.Objects.ObjectQuery`1[Dcip.Ams.BO.EquipmentWarranty]'
cannot be converted to type
'System.Data.Entity.DbSet`1[Dcip.Ams.BO.EquipmentWarranty]'
This is a pretty common error to do with the new proxies, and I know that I can fix it by setting:
MyContext.Configuration.ProxyCreationEnabled = false;
But that defeats the purpose of a lot of what I am trying to do. Is there a better way?
I would suggest Disable Proxy Creation only in the place where you don't need or is causing you trouble. You don't have to disable it globally you can just disable the current DB context via code...
[HttpGet]
[WithDbContextApi]
public HttpResponseMessage Get(int take = 10, int skip = 0)
{
CurrentDbContext.Configuration.ProxyCreationEnabled = false;
var lista = CurrentDbContext.PaymentTypes
.OrderByDescending(x => x.Id)
.Skip(skip)
.Take(take)
.ToList();
var count = CurrentDbContext.PaymentTypes.Count();
return Request.CreateResponse(HttpStatusCode.OK, new { PaymentTypes = lista, TotalCount = count });
}
Here I only disabled the ProxyCreation in this method, because for every request there is a new DBContext created and therefore I only disabled the ProxyCreation for this case .
Hope it helps
if you have navigation properties and you do not want make them non virtual, you should using JSON.NET and change configuration in App_Start to using JSON not XML!
after install JSON.NET From NuGet, insert this code in WebApiConfig.cs in Register method
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
If you have navigation properties make them non virtual. Mapping will still work but it prevents the creation of Dynamic Proxy entities which cannot be serialized.]
Not having lazy loading is fine in a WebApi as you don't have a persistent connection and you ran a .ToList() anyway.
I just disabled proxy classes on a per needed basis:
// GET: ALL Employee
public IEnumerable<DimEmployee> Get()
{
using (AdventureWorks_MBDEV_DW2008Entities entities = new AdventureWorks_MBDEV_DW2008Entities())
{
entities.Configuration.ProxyCreationEnabled = false;
return entities.DimEmployees.ToList();
}
}
Add the following code in Application_Start function of Global.asax.cs:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters
.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
This instruct the API to serialize every response into JSON and remove XML responses.
In my case the object being returned had a property within it with a type that did not have an argumentless/default constructor. By adding a zero-argument constructor to that type the object could be serialized successfully.
I had the same problem and my DTO was missing an parameter less constructor.
public UserVM() { }
public UserVM(User U)
{
LoginId = U.LoginId;
GroupName = U.GroupName;
}
First constructor was missing.
I got this error message and it turns out the problem was that I had accidentally set my class to use the same serialized property name for two properties:
public class ResultDto
{
//...
[JsonProperty(PropertyName="DataCheckedBy")]
public string ActualAssociations { get; set; }
[JsonProperty(PropertyName="DataCheckedBy")]
public string ExpectedAssociations { get; set; }
//...
}
If you're getting this error and you aren't sending entities directly through your API, copy the class that's failing to serialize to LINQPad and just call JsonConvert.SerializeObject() on it and it should give you a better error message than this crap. As soon as I tried this it gave me the following error message: A member with the name 'DataCheckedBy' already exists on 'UserQuery+ResultDto'. Use the JsonPropertyAttribute to specify another name.
After disable Proxy Creation, use eager loading (Include()) to load the proxy object.
In my Project EntityCollection returned from the WebApi action method.
Configuration.ProxyCreationEnabled = false not applicable. I have tried the below approach it is working fine for me.
Control Panel.
2.Turn on Windows Features on or off
Choose Internet Information Service
Check all the World Wide Web Components it would be better to check all the components in IIS.
Install the components.
Go to (IIS) type inetmgr in command prompt.
select the published code in the Virtual directory.
Convert into application
Browse it the application.
The answer by #Mahdi perfectly fixes the issue for me, however what I noticed is that if my Newtonsoft.JSON is 11.0 version then it doesn't fix the issue, but the moment I update Newtonsoft.JSON to latest 13.0 it starts working.

autofac registration issue in release v2.4.5.724

I have the following registration
builder.Register<Func<Type, IRequestHandler>>(
c => request => (IRequestHandler)c.Resolve(request));
Basically I am trying to register a factory method that resolves an instance of IRequestHandler from a given type.
This works fine until the version 2.4.3.700. But now I am getting a the following error..
Cannot access a disposed object.
Object name: 'This resolve operation has already ended. When
registering components using lambdas,
the IComponentContext 'c' parameter to
the lambda cannot be stored. Instead,
either resolve IComponentContext again
from 'c', or resolve a Func<> based
factory to create subsequent
components from.'.
UPDATE
I was trying to limit autofac's exposure to the rest of the projects in the solution. Nick, thanks for the hint, now my registration looks like this...
builder.Register<Func<Type,IRequestHandler>>(c =>
{
var handlers = c.Resolve<IIndex<Type,RequestHandler>>();
return request => handlers[request];
});
The c in this expression is a temporary, so this code while previously functional, is broken. Autofac 2.4.5 detects this problem while earlier versions silently ignored it.
To fix the issue, explicitly resolve IComponentContext:
builder.Register<Func<Type, IRequestHandler>>(c => {
var ctx = c.Resolve<IComponentContext>();
return request => (IRequestHandler)ctx.Resolve(request));
});
The functionality you're emulating here might be better represented using keys and indexes, e.g. see Interrupted chain of IoC or http://code.google.com/p/autofac/wiki/TypedNamedAndKeyedServices.
I had a similar problem as the user6130. I wanted to avoid using IIndex in my class implementation and pass in a service resolver into my constructor instead.
So now I have my service implementation with the following constructor:
public MvcMailer(Converter<string, MailerBase> mailerResolver)
{
_resolver = mailerResolver;
}
I wanted to used keyed services without directly relying on the Autofac namespace. I was getting the same error until I restructured the configuration as such.
1) Scan for all my mailer implementations and index via class name (could be improved)
builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly())
.Where(t => t.Name.EndsWith("Mailer")).Keyed<Mvc.Mailer.MailerBase>(t => t.Name.Replace("Mailer", "").ToLower());
2) Register the converter in Autofac config
builder.Register<System.Converter<string,Mvc.Mailer.MailerBase>>(c => {
var all = c.Resolve<Autofac.Features.Indexed.IIndex<string,Mvc.Mailer.MailerBase>>();
return delegate(string key)
{
return all[key];
};
});
3) Register like other types of components and let Autofac handle the Converter injection
builder.RegisterType<Mailers.MvcMailer>().As<Mailers.IMailer>();

Is it possible in Autofac to resolve all services for a type, even if they were registered with a name or key?

In Autofac one can do the following to get all registered services:
IEnumerable<MyClass> all = Context.Resolve<IEnumerable<MyClass>>()
However, this does not include those which were registered as named services.
Looking at the Autofac source it seems this is because services are queried for resolution based on either a TypedService or a KeyedService.
Is there a way to resolve all services to an IEnumerable, irrespective of whether they were registered with a name or not?
The best option here is to register the items using both the key and the regular 'typed' service:
builder.Register<CupOfT>()
.As<IBeverage>()
.Keyed<IBeverage>("someKey");
You can then just resolve IEnumerable<IBeverage> to get the result you're after, while resolving them by key (or name) is also supported.
If you're concerned about maintaining a particular default registration for IBeverage just use PreseveExistingDefaults() on the others (or make sure your intended default is registered last).
HTH!
Nick
I've written a method which appears to work; I'd appreciate feedback if there's a built-in way to do this in Autofac. In the below example, the field _context is of type IComponentContext.
public IEnumerable<T> ResolveAll<T>()
{
// We're going to find each service which was registered
// with a key, and for those which match the type T we'll store the key
// and later supplement the default output with individual resolve calls to those
// keyed services
var allKeys = new List<object>();
foreach (var componentRegistration in _context.ComponentRegistry.Registrations)
{
// Get the services which match the KeyedService type
var typedServices = componentRegistration.Services.Where(x => x is KeyedService).Cast<KeyedService>();
// Add the key to our list so long as the registration is for the correct type T
allKeys.AddRange(typedServices.Where(y => y.ServiceType == typeof (T)).Select(x => x.ServiceKey));
}
// Get the default resolution output which resolves all un-keyed services
var allUnKeyedServices = new List<T>(_context.Resolve<IEnumerable<T>>());
// Add the ones which were registered with a key
allUnKeyedServices.AddRange(allKeys.Select(key => _context.ResolveKeyed<T>(key)));
// Return the total resultset
return allUnKeyedServices;
}
It would appear that you can combine the As<T>() methods and Named<T>() methods as shown below:
[TestMethod]
public void ResolveTests()
{
var builder = new ContainerBuilder();
builder.RegisterType<ClassA1>().As<IClassA>().Named<IClassA>("1");
builder.RegisterType<ClassA2>().As<IClassA>().Named<IClassA>("2");
builder.RegisterType<ClassA3>().As<IClassA>().Named<IClassA>("3");
var container = builder.Build();
var allInstances = container.Resolve<IEnumerable<IClassA>>();
allInstances.Count().Should().Be(3);
container.ResolveNamed<IClassA>("1").Should().BeAssignableTo<ClassA1>();
container.ResolveNamed<IClassA>("2").Should().BeAssignableTo<ClassA2>();
container.ResolveNamed<IClassA>("3").Should().BeAssignableTo<ClassA3>();
}

Using Moq with Windsor -- Object of Type Moq.Mock[IFoo] cannot be converted to IFoo

I'm trying to set up some Moq repositories to test my service with Castle Windsor as my IOC. Mu service depends on IFoo, so I'm creating a moq instance that implements IFoo and injecting it into the container like so:
_container.AddComponent("AutoBill",
typeof (AutoBillService), typeof (AutoBillService));
var mockUserRepository = new Mock<IUserRepository>();
var testUser = new User()
{
FirstName = "TestFirst",
LastName = "TestLast",
UID=1
};
mockUserRepository.Setup(repo => repo.GetUser(testUser.UID))
.Returns(testUser);
_container.Kernel.AddComponentInstance("UserRepository",
typeof(IUserRepository), mockUserRepository);
var service = _container.Resolve<AutoBillService>(); //FAIL
Doing this gives me an exception:
System.ArgumentException: Object of type 'Moq.Mock`1[IUserRepository]' cannot be converted to type 'IUserRepository'
Can anyone see what I'm doing wrong?
You should pass mockUserRepository.Object instead of mockUserRepository.
This would be a lot more evident if you used the strongly typed API:
_container.Register(Component
.For<IUserRepository>()
.Instance(mockUserRepository.Object));
This compiles because the Object property implements IUserRepository.
I head the same problem with Castle Windsor.
A dinamyc initialization with method:
container.Register(Component.For<IUserRepository>()
.Instance(mockUserRepository.Object));
didn't work until I removed from my caslteRepository.config file pre-initialized repositories (like your IUserRepository) and left container "empty" from repositories.