Don't miss out Virtual Happy Hour this Friday (April 26).

Try our conversational search powered by Generative AI!

Removing concrete instance from IOC

Vote:
 

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

#182085
Sep 08, 2017 17:01
Vote:
 

Add interceptor around this and "silence" original one. Had exactly the same issue with epi forms view external resource provider.

#182090
Sep 08, 2017 17:52
Vote:
 

You can always eject all current registered instances, then register yours. 

#182092
Sep 08, 2017 19:18
Vote:
 

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 ;)

#182093
Sep 08, 2017 20:33
Vote:
 

Yes, but Gosso wanted to show who is the boss (tip: he is)

#182099
Sep 09, 2017 1:23
Vote:
 

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"));
#182101
Sep 09, 2017 10:10
Vote:
 

Aaaand still don't you want to try intercept? ;))

#182102
Sep 09, 2017 11:44
Vote:
 

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

#182128
Sep 11, 2017 14:32
Vote:
 

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.

#182134
Sep 11, 2017 15:29
Vote:
 

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.

#182135
Sep 11, 2017 15:31
Vote:
 

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...

#182140
Sep 11, 2017 16:17
Vote:
 

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.

#182141
Sep 11, 2017 16:19
Vote:
 

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>.... 

#182163
Sep 12, 2017 8:30
Vote:
 

really? but what episerver is asking from container when it needs to format? `INoficationFormatter` or `ApprovalHtmlEmailFormatter`?

#182164
Sep 12, 2017 8:38
Vote:
 

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. 

#182165
Sep 12, 2017 8:50
Vote:
 

can you give me hint about code location thatš doing this?

#182167
Sep 12, 2017 8:59
Vote:
 

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"
        };
      }
    }

....

}

#182169
Sep 12, 2017 9:09
Vote:
 

ok, that's declaration. but where and how this is used later on? who and how is asking for an isntance of formatter?

#182170
Sep 12, 2017 9:11
Vote:
 

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. 

#182174
Edited, Sep 12, 2017 9:24
Vote:
 

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?

#182178
Sep 12, 2017 9:51
Vote:
 

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. 

#182189
Sep 12, 2017 10:34
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.