Is it possible to prevent Delegate Factory creation in Autofac? - dependency-injection

I am updating a library that uses Autofac so that, in addition to the original configuration file (registered via Autofac), it can optionally take a function to accomplish that same goal (again, registered via Autofac). The original is something like this:
public MyClass(ConfigFile config = null)
{
this._activatorLoader = a => {
// old config code here...
}
}
The updated version I'd like is:
public MyClass(
Func<Input, IList<Activator>> activatorLoader = null,
ConfigFile config = null)
{
if (activatorLoader != null)
{
this._activatorLoader = activatorLoader;
}
else
{
this._activatorLoader = a => {
// old config code here...
}
}
}
The problem is that Autofac is seeing my request for a list of something and always providing the function. I tried switching to a delegate and get the same problem:
public delegate IList<Activator> ActivatorLoader(Input input);
public MyClass(
ActivatorLoader activatorLoader = null,
ConfigFile config = null)
{
if (activatorLoader != null)
{
this._activatorLoader = activatorLoader;
}
else
{
this._activatorLoader = a => {
// old config code here...
}
}
}
The loading of the activators must still be delayed, I'd like the flexibility of registering any function based on the situation, and old code (without an activator loader registered) should still work. Is there any way to prevent Autofac from autogenerating the Func?
The class will be instantiated through dependency injection in another class. At a later time, the activator loading code will be triggered (if needed).
var myObject = conatiner.Resolve<MyClass>();
// time passes...
myObject.DoActivatorLoading();
The primary goal is to prevent Autofac from creating the Func<Input, IList<Activator>> if it is not explicitly set. Is this possible?

Perhaps, it will be easier to create a separate class for activator loading ?
public class ActivatorsLoader
{
public IList<Activator> Load(Input input)
{
///
}
}
///
public Class(
ActivatorsLoader activatorsLoader = null,
ConfigFile config = null)
{
///
}
But it would be good to see the use cases of your Class.

Related

Auto-registration with a RegistrationSource in Autofac

Here's what I'm doing so far (code simplified):
public class MyRegistrationSource : IRegistrationSource
{
public MyRegistrationSource(ContainerBuilder builder /*,...*/)
{
// ...
this.builder = builder;
}
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
// Some checks here
var interfaceType = serviceWithType.ServiceType;
var implementorType = FindTheRightImplementor(interfaceType);
if (myRegisterConditionSatisfied)
{
return Register(implementorType, interfaceType);
}
return Empty;
}
private IEnumerable<IComponentRegistration> Register(Type concrete, Type #interface)
{
var regBuilder = builder.RegisterType(concrete).As(#interface).IfNotRegistered(#interface);
return new[] { regBuilder.CreateRegistration() };
}
}
Then, at startup I'm doing something like
builder.RegisterSource(
new NonRegisteredServicesRegistrationSource(builder/*, ...*/));
The above is intended to register those matching services only when there's no previous registration. I tried doing the registration without using the ContainerBuilder but couldn't get it to work.
This is working but are there any issues in passing-in the ContainerBuilder instance to the RegistrationSource?
Thanks!
I'd probably argue against passing in a ContainerBuilder.
Every type you register in your source will add a callback to a list of callbacks inside the Container Builder which will never get cleared, potentially creating a memory leak.
I'd suggest calling the static method RegistrationBuilder.ForType instead, which will give you a fluent builder and should let you subsequently call CreateRegistration as you are now.
You can see some pretty good examples of how do this in our Moq integration:
var reg = RegistrationBuilder.ForType(concrete)
.As(#interface)
.CreateRegistration();
Also, I don't believe IfNotRegistered will have any effect when used outside the context of a ContainerBuilder. You should use the provided registrationAccessor parameter to the registration source to look up a TypedService to see if it has already been registered:
var isRegistered = registrationAccessor(new TypedService(#interface)).Any();

MVC simple insert not updating database

The is driving me nuts. Im trying to do a "simple" record insert and I can only get it to work if I store the context in a variable or create a local context. I tried to keep the context and model object tied together but no luck so far.
public class TransactionDataAccessLayer
{
public cartableContext transactionContext
{
get
{
return new cartableContext();
}
}
}
class TransactionBusinessLayer
{
Cardata newCar = new Cardata();
public void addCar(Cardata cd)
{
try
{
//this works. Storing the context in ctc2 seems to make it work???
TransactionDataAccessLayer tdal = new TransactionDataAccessLayer();
cartableContext ctc2 = tdal.transactionContext;
ctc2.cardata.Add(cd);
ctc2.SaveChanges();
//this does not work
tdal.transactionContext.cardata.Add(cd);
tdal.transactionContext.Entry(cd).State = EntityState.Modified;
tdal.transactionContext.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
}
}
}
In C#, properties are basically just fancy methods, designed to make it easier to access private fields. Therefore, returning a new Context in your getter will do just that; return a new one each time it is accessed. To preserve state, you need to contain your context in a private field, like so:
public class TransactionDataAccessLayer
{
private cartableContext _transactionContext;
public cartableContext transactionContext
{
get
{
if (_transactionContext == null)
_transactionContext = new cartableContext();
return _transactionContext;
}
}
}

Orchard CMS ContentManager.New<>() Specified Cast Was Invalid

I am in the early stages of developing a new module.
Much of it is laid out in terms of the models etc. Also have the migrations all set up and my database now has the tables for my module.
I am encountering the following error when calling ContentManager.New<myPart> and would like some help please.
Error is this:
An unhandled exception has occurred and the request was terminated. Please refresh the page. If the error persists, go back
Specified cast is not valid.
System.InvalidCastException: Specified cast is not valid.
at Orchard.ContentManagement.ContentCreateExtensions.New[T]
(IContentManager manager, String contentType)
The chunk of code that fires the exception is this:
public static T New<T>(this IContentManager manager, string contentType) where T : class, IContent {
var contentItem = manager.New(contentType);
if (contentItem == null)
return null;
var part = contentItem.Get<T>();
if (part == null)
throw new InvalidCastException();
return part;
}
Here are the various parts to my module that are related to the operation i am struggling with:
ContentPart
public class GoogleMapsSettingsPart : ContentPart<GoogleMapsSettingsPartRecord>
{
public string ApiKey {
get { return Record.ApiKey; }
set { Record.ApiKey = value; }
}
}
ContentPartRecord
public class GoogleMapsSettingsPartRecord : ContentPartRecord
{
public virtual string ApiKey { get; set; }
}
Handler
public GoogleMapsSettingsPartHandler(IRepository<GoogleMapsSettingsPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
Migration for this table
// Settings Table
SchemaBuilder.CreateTable("GoogleMapsSettingsPartRecord", table => table
.ContentPartRecord()
.Column("ApiKey", DbType.String, c => c.WithLength(60))
);
Some of the code from the controller for this model etc
public AdminController(IContentManager contentManager, IShapeFactory shapeFactory, IServiceLocatorService serviceLocatorService, INotifier notifier)
{
_contentManager = contentManager;
_serviceLocatorService = serviceLocatorService;
_notifier = notifier;
Shape = shapeFactory;
T = NullLocalizer.Instance;
}
/// <summary>
/// Display Settings
/// </summary>
/// <returns></returns>
public ActionResult Settings()
{
var settings = _serviceLocatorService.GoogleMapsSettings;
var editor = CreateSettingsEditor(settings);
var model = _services.ContentManager.BuildEditor(settings);
return View((object)model);
}
Finally - the Services where my call throws this exception
private GoogleMapsSettingsPart _settings;
public GoogleMapsSettingsPart GoogleMapsSettings
{
get {
if (_settings == null)
{
_settings = _contentManager.Query<GoogleMapsSettingsPart, GoogleMapsSettingsPartRecord>().List().FirstOrDefault();
if (_settings == null)
{
_settings = _contentManager.New<GoogleMapsSettingsPart>("GoogleMapsSettings");
}
}
return _settings;
}
}
The actual line where the exception happens is _settings = _contentManager.New<GoogleMapsSettingsPart>("GoogleMapsSettings");
I have tried all sorts of stuff in place of "GoogleMapsSettings" though nothing is working.
I'm pretty sure at this point it's something simple, though it's avoiding me..My limited knowledge of Orchard is stumping me
Any help would be appreciated :)
The exception is thrown because your content type does not have the part you specified to get.
_contentManager.New<GoogleMapsSettingsPart>("GoogleMapsSettings");
This method call creates a new content item of type GoogleMapSettings and gets the content item as a GoogleMapsSettingsPart. However, it seems that GoogleMapSettings content type does not have a GoogleMapsSettingsPart. That's why the exception gets thrown here.
var part = contentItem.Get<T>();
if (part == null)
throw new InvalidCastException();
You must either attach the part dynamically to your content type or do it in a migration (or manually in the admin, but that's not a good idea). Your migration should look like this.
this.ContentDefinitionManager.AlterTypeDefinition("GoogleMapsSettings",
alt => alt
.WithPart("GoogleMapsSettingsPart");
Ok, so I fixed it...
My understanding of how Orchard works is still very much in the learning stages.
for this particular operation I didn't want to have a content type in the admin - though not sure why after adding the ContentType it still didn't work...
anyway, adding the lines below to my handler took care of the rest. I believe it's actually creating a temporary type so one isn't needed in the system.
public GoogleMapsSettingsPartHandler(IRepository<GoogleMapsSettingsPartRecord> repository)
{
Filters.Add(new ActivatingFilter<GoogleMapsSettingsPart>("GoogleMapsSettings"));
Filters.Add(StorageFilter.For(repository));
Filters.Add(new TemplateFilterForRecord<GoogleMapsSettingsPartRecord>("GoogleMapsSettings", "Parts/GoogleMapsSettings"));
}
I'v got the same error, but in my case it was everything ok with migration class.
The reason was unlucky merge, which deleted my driver class of my part.
Just look at this code of Activating method of ContentPartDriverCoordinator class. In my case there was no partInfo for my content part and resulted part became ContentPart, so casting throws an exception
var partInfos = _drivers.SelectMany(cpp => cpp.GetPartInfo()).ToList();
foreach (var typePartDefinition in contentTypeDefinition.Parts) {
var partName = typePartDefinition.PartDefinition.Name;
var partInfo = partInfos.FirstOrDefault(pi => pi.PartName == partName);
var part = partInfo != null
? partInfo.Factory(typePartDefinition)
: new ContentPart { TypePartDefinition = typePartDefinition };
context.Builder.Weld(part);
}

RavenDB and IDocumentStoreListener

I am trying to add an autoincrement to a simple model via an IDocumentStoreListener. I have found that the documentation regarding implementation of this feature is fairly sparse (any pointers would be greatly appreciated). I have been trying to follow this blog post but it appears to be out of date. When i try to implement
store = new EmbeddableDocumentStore
{
RunInMemory = true
}
.RegisterListener(new AuditableEntityListener(() => "Test User"))
.Initialize();
I get a build error stating "Cannot convert lambda expression to type Raven.Client.IDocumentStore because it is not a delegate type".
I managed to get it to build by using this code
store = new EmbeddableDocumentStore
{
RunInMemory = true
}
.RegisterListener(new AuditableEntityListener(store ))
.Initialize();
The code for the listener is as follows
public class PublicIdStoreListener : IDocumentStoreListener
{
HiLoKeyGenerator generator;
IDocumentStore store;
public PublicIdStoreListener(IDocumentStore store)
{
this.store = store;
generator = new HiLoKeyGenerator(store, "verifications", 1024);
}
public void AfterStore(string key, object entityInstance, RavenJObject metadata)
{
throw new NotImplementedException();
}
public bool BeforeStore(string key, object entityInstance, RavenJObject metadata)
{
var verification = entityInstance as VerifyAccountModel;
if (verification.PublicId == "0")
{
verification.PublicId = generator.GenerateDocumentKey(store.Conventions, entityInstance);
}
return false;
}
}
However, when i run the application it hits the PublicIdStoreListener when any document is stored, not just the VerifyAccountModel, which causes the application to throw an exception.
I was wondering if anyone can point me in the right direction on this as I am confused as to how this is actually supposed to be implemented. Thanks in advance.
EDIT
I updated the documentlistener to the following
public bool BeforeStore(string key, object entityInstance, RavenJObject metadata)
{
if (entityInstance.GetType() == new VerifyAccountModel().GetType())
{
var verification = entityInstance as VerifyAccountModel;
if (verification.PublicId == "0")
{
verification.PublicId = generator.GenerateDocumentKey(store.Conventions, entityInstance);
}
}
return true;
}
UPDATE
I figured out that i cant attach the store via RegisterListener in the same line that it is instantiated. It has to be done afterwards otherwise the store is still null when passed in. Thank you for your help.
I am not sure if there's a way to register the listener to only fire for certain types, but you can certainly structure your code to only process VerifyAccountModel entities.
var verification = entityInstance as VerifyAccountModel;
if (verification == null)
return false; // We can't do anything, just let it pass through
Also, my understanding is that you should return true when you make a change, false if no change was made. This determines whether the entity needs to be re-serialized. If that is correct, the whole thing might be restructured as follows.
var verification = entityInstance as VerifyAccountModel;
if (verification != null && verification.PublicId == "0")
{
verification.PublicId = generator.GenerateDocumentKey(store.Conventions, entityInstance);
return true; // change made, re-serialize
}
return false; // no change made

Windsor Interceptor Exception

I have a Windsor container that I'm using an InterceptorSelector and a LazyComponentLoader with.
My InterceptorSelector returns an InterceptorReference to my InterceptorAdapter class that looks like this
public class InterceptorAdapter<T> : Castle.DynamicProxy.IInterceptor, IOnBehalfAware where T : IMyType
{
private readonly T interceptor;
public InterceptorAdapter(T interceptor)
{
this.interceptor = interceptor;
}
....
}
so the InterceptorSelector does
public InterceptorReference[] SelectInterceptors(ComponentModel model, InterceptorReference[] interceptors)
{
var results = new List<InterceptorReference>();
....
foreach (var interceptorType in someInterceptorTypes)
{
Type interceptorAdapterType = typeof(InterceptorAdapter<>).MakeGenericType(interceptorType);
if (kernel.GetHandler(interceptorAdapterType) == null)
{
// need to do this or Castle complains it can't create my
// interceptor...I guess LazyComponentLoaders don't work for InterceptorReferences?...
// I suspect the problem may be here...
kernel.Register(lazyComponentLoader.Load(null, interceptorAdapterType, null));
}
results.Add(InterceptorReference.ForType(interceptorAdapterType));
}
return results.ToArray();
}
Things work great the first time my InterceptorSelector returns an InterceptorReference. The next time, when it returns an InterceptorReference to an InterceptorAdapter with a different generic type argument, I get
Castle.MicroKernel.ComponentRegistrationException occurred
Message=There is a component already registered for the given key interceptor
Source=Castle.Windsor
StackTrace:
at Castle.MicroKernel.SubSystems.Naming.DefaultNamingSubSystem.Register(String key, IHandler handler) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\SubSystems\Naming\DefaultNamingSubSystem.cs:line 75
InnerException:
My LazyComponentLoader simply does
public IRegistration Load(string key, Type service, IDictionary arguments)
{
ComponentRegistration<object> component = Component.For(service).Named(key);
if (arguments != null)
{
// merge is an extension method to merge dictionaries.
component.DynamicParameters((k, d) => d.Merge(arguments));
}
return component;
}
My container setup looks like
var container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();
container.AddFacility("factories", new FactorySupportFacility());
container.Register(Component.For<ILazyComponentLoader>().ImplementedBy<MyLazyComponentLoader>().LifeStyle.Singleton);
container.Kernel.ProxyFactory.AddInterceptorSelector(new InterceptorSelector(container.Kernel, container.Resolve<ILazyComponentLoader>()));
Any suggestions?
Thanks!
This seems to be a problem in the way Castle resolves Interceptors using an LazyComponentLoader.
Changing my code in the selector to do:
if (kernel.GetHandler(interceptorType) == null)
{
// need to do this or Castle complains it can't create my
// interceptor...I guess LazyComponentLoaders don't work for InterceptorReferences?...
// I suspect the problem may be here...
kernel.Register(lazyComponentLoader.Load(null, interceptorType, null));
}
Type interceptorAdapterType = typeof(InterceptorAdapter<>).MakeGenericType(interceptorType);
if (kernel.GetHandler(interceptorAdapterType) == null)
{
// need to do this or Castle complains it can't create my
// interceptor...I guess LazyComponentLoaders don't work for InterceptorReferences?...
// I suspect the problem may be here...
kernel.Register(lazyComponentLoader.Load(null, interceptorAdapterType, null));
}
Seems to fix the problem.

Resources