Add interceptor around this and "silence" original one. Had exactly the same issue with epi forms view external resource provider.
You can always eject all current registered instances, then register yours.
it might work in cases when you really want to dominate. there are also cases when you need to suppress that one particular instance (shut it off) but leave rest as-is. and in most cases you don't know who else is there ;)
yes, thanks,
context.StructureMap().Model.EjectAndRemove(typeof(INotificationFormatter)); //addin mine context.StructureMap().Configure(ce => { ce.For<INotificationFormatter>().Singleton().Use<CustomApprovalHtmlEmailFormatter>(); });
works, but i will look for finding the explicit concrete class, so i don't remove all other INoficationFormatter out there.
Bellow is not working though
context.StructureMap().Model.EjectAndRemovePluginTypes(type => type.Namespace != null && type.Namespace.Equals("EPiServer.Cms.Shell.UI.Approvals.Notifications") && type.Name.Equals("ApprovalHtmlEmailFormatter"));
having no luck here
Tried Intercept...
context.StructureMap().Configure(container => { container.For<INotificationFormatter>().DecorateAllWith(instance => proxyGenerator.CreateInterfaceProxyWithTargetInterface(instance, new RemoveDefaultApprovalFormatterInterceptor())); });
But it is NOT hooking up on specific class, just other INotificationFormatters
DEFAULT IoC PuginType and ConcreteType is "EPiServer.Cms.Shell.UI.Approvals.Notifications.ApprovalHtmlEmailFormatter,EPiServer.Cms.Shell.UI, Version=10.9.1.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7" SCOPE: StructureMap.Pipeline.SingletonLifecycle
When i remove the type with for example context.StructureMap().Model.EjectAndRemove(t.PluginType); the SCOPE changes to Transient (meaning it reinitiated on every call)
Removing permanentaly seems not possible?...
Any Idea how to intercept without having the concrete class since it is internal?
hmm
you can get to work this with code below:
[InitializableModule] [ModuleDependency(typeof(DependencyResolverInitialization))] public class EPiServerFormsConfigurationModule : IConfigurableModule { public void Initialize(InitializationEngine context) { } public void Uninitialize(InitializationEngine context) { } public void ConfigureContainer(ServiceConfigurationContext context) { context.Services.Intercept<INotificationFormatter>((locator, service) => new FixedApprovalHtmlEmailFormatter(service)); } } public class FixedApprovalHtmlEmailFormatter : INotificationFormatter { private readonly INotificationFormatter _inner; public FixedApprovalHtmlEmailFormatter(INotificationFormatter inner) { _inner = inner; } public Task<IEnumerable<FormatterNotificationMessage>> FormatMessagesAsync(IEnumerable<FormatterNotificationMessage> notifications, string recipient, NotificationFormat format, string channelName) { return _inner.FormatMessagesAsync(notifications, recipient, format, channelName); } public IEnumerable<string> SupportedChannelNames => new[] { "test" }; }
in ideal case I would use `locator.GetInstance<INotificationFormatter>()` in `Intercept()` method, but then you will get circular dependencies and blow up from structuremap.
and forgot to mention that `DependencyResolverInitialization` module is my own module where I set dependency resolvers to Mvc and WebApi (created out of epi's structuremap container), but I guess you don't need explicitly chain to that phase of the initialization pipeline.
thx, valdis, your code illustrates it and works in "INotificationFormatter", but no luck because when i run context.StructureMap().WhatDoIHave(); the object "ApprovalHtmlEmailFormatter" is not attached to "INotificationFormatter". It is attached to it self concret class ApprovalHtmlEmailFormatter. Even if it is an INotificationFormatter.
So the question how do i "attach" to ApprovalHtmlEmailFormatter when it is not accessable...
Interceptors are generic concept applied to all classes. That's why you have _inner field. The only way to check which formatter was passed in is to use `GetType()` and then compare it's name in stringly typed way. There is no compile time access for you to that class.
I get the concept, thats cool. The problem is how to configure the object, since plugintype is not INoficationFormatter in Container, it is "ApprovalHtmlEmailFormatter", but since it is internal class, you cant ce.Intercept<ApprovalHtmlEmailFormatter>....
really? but what episerver is asking from container when it needs to format? `INoficationFormatter` or `ApprovalHtmlEmailFormatter`?
ApprovalHtmlEmailFormatter, anyhow, it shouldn't be this hard to either turn of default email, or brand the email, i will contact the support and give them this feedback.
You can check with devtools IOC, search for ApprovalHtmlEmailFormatter.
the namespace is EPiServer.Cms.Shell.UI.Approvals.Notifications in EPiServer.Cms.Shell.UI.dll
declared:
namespace EPiServer.Cms.Shell.UI.Approvals.Notifications { [ServiceConfiguration(typeof (INotificationFormatter), Lifecycle = ServiceInstanceScope.Singleton)] internal class ApprovalHtmlEmailFormatter : HtmlEmailFormatterBase, INotificationFormatter { private readonly ApprovalEmailService _approvalEmailService; public IEnumerable<string> SupportedChannelNames { get { return (IEnumerable<string>) new string[1] { "epi-approval" }; } }
....
}
ok, that's declaration. but where and how this is used later on? who and how is asking for an isntance of formatter?
okej, here is where the DI magic begins (param IEnumerable<INotificationFormatter> formatters)
public DefaultNotificationDispatcher(INotificationUserRepository userRepository, INotificationRepository repository, IEnumerable<INotificationFormatter> formatters, IEnumerable<INotificationProvider> providers) { this._repository = repository; this._formatters = formatters ?? Enumerable.Empty<INotificationFormatter>(); this._providers = providers ?? Enumerable.Empty<INotificationProvider>(); this._userRepository = userRepository; } more code internal INotificationFormatter GetFormatter(string channelName) { List<INotificationFormatter> list = this._formatters.Where<INotificationFormatter>((Func<INotificationFormatter, bool>) (x => x.SupportedChannelNames.Contains<string>(channelName))).ToList<INotificationFormatter>(); if (list.Count > 1) { DefaultNotificationDispatcher.Logger.Warning("Multiple Formatters found for Channel(\"{0}\")", new object[1] { (object) (channelName ?? "") }); return (INotificationFormatter) null; } if (list.Count >= 1) return list.Single<INotificationFormatter>(); DefaultNotificationDispatcher.Logger.Warning("No Formatter found for Channel(\"{0}\")", new object[1] { (object) (channelName ?? "") }); return (INotificationFormatter) null; }
"Multiple Formatters found for Channel (epi-approvals)" is the one i want to get around.
hmm.. interesting. interceptors should not be as separate registrations in the IoC registry. You should still see just those that were implicitly registered (via attributes) or explicitly (via registries or directly). those are more for composition pipeline for structuremap itself. when you ask a `IEnumerable<Fromatters>` in let's say start page controller - do you see your interceptor formatter as another list item there?
oooh, when i run it in a controller (i see three FixedEmailFormatter) also see that it is another type (EPiServer.Cms.Shell.UI.Approvals.Notifications.ApprovalNotificationFormatter not ApprovalHtmlEmailFormatter), so thats why, been stearing at wrong class, the strange thing is that when i reflect, it not there.
Anyhow, that explains it, thanks Valdis, i'll see if this is something i go further with.
Hi, need some help,
How do i remove this internal class when it is registred like this in EPiServer.Cms.Shell.UI.dll
[ServiceConfiguration(typeof (INotificationFormatter))]
internal class ApprovalHtmlEmailFormatter
There is no room for two, and i'd like to use mine... =) Since it is internal it is not visible in my code...
Any help appreciated.
-Regards