I use ModuleManager load a module, like this Class:
public class LoadModule
{
private static var info:IModuleInfo;
private static var display:IVisualElement;
private static var downloadBar:ProgressBar;
private static var parent:Group;
public function LoadModule()
{
}
//load module
public static function load(url:String, parent:Group, bar:Boolean = true):void {
LoadModule.parent = parent;
info = ModuleManager.getModule(url);
info.addEventListener(ModuleEvent.READY, readyHandler);
info.addEventListener(ModuleEvent.SETUP, setupHandler);
info.addEventListener(ModuleEvent.ERROR, errorHandler);
info.load(null, null, null, parent.moduleFactory);
}
//add display object
private static function readyHandler(event:ModuleEvent):void {
LoadModule.display = event.currentTarget.factory.create() as IVisualElement;
parent.addElement(LoadModule.display);
}
private static function setupHandler(event:ModuleEvent):void {
}
//unload module
public static function unload():void {
if (LoadModule.info != null) {
LoadModule.info.addEventListener(ModuleEvent.UNLOAD, unloadHandler);
LoadModule.info.unload();
if (parent.getElementIndex(LoadModule.display) != -1) {
parent.removeAllElements();
LoadModule.display = null;
}
}
}
private static function unloadHandler(event:ModuleEvent):void {
LoadModule.info.removeEventListener(ModuleEvent.UNLOAD,unloadHandler);
trace("unloadModue");
}
//
private static function progresshandler(event:ModuleEvent):void {
downloadBar.label = "haved" + Math.round((event.bytesLoaded /event.bytesTotal) * 100) + "%";
}
private static function errorHandler(event:ModuleEvent):void {
throw Error(event.errorText);
}
public static function setDownloadbar(downloadBar:ProgressBar):void {
LoadModule.downloadBar = downloadBar;
}
}
Then i load a module and unload a module:
LoadModule.unload(); // 1
LodModule.load('..one.swf', parent);
LoadModule.unload(); //2
LodModule.load('...one.swf', parent);
In theory, It's only one module in my application, and I use "PopUpManager" pop a box, it shoud be one box. But, in fact, It's pop tow box.
I use Flash builder debug this code, and It does not notice me unloade swf..
I guess, It has tow module in appliction.
So, I need help. How to unload module in ModuleManager. I wish one module in application , not tow.
Thanks.
If I understand the question correctly, it sounds like you are having trouble unloading your module. There's a great Adobe resource that can help you solve these issues. A few considerations:
"If you have a module that does not unload, the steps to diagnose the problem are:
1) Make sure the module is being loaded into a child applicationDomain (use default parameters for the load() method in most cases)
2) Use the profiler to make sure there are no references to objects in the module."
If you reference any objects in the module, the module will not unload. You will want to check that the following areas make no reference to the module in question:
Styles
Resources
ExternalInterface.addCallback functions
Timers and Timer mechanisms
Listeners
Focus
Remote Objects
Loaded images
Related
UPDATE 2: FIXED THE CODE at the end
I have the abp.io service below with 2 parameters in the constructor instantiated via DI.
One of them, IOutcomeWriter, has 2 implementations.
I'd like to define at runtime which of the implementations of IOutcomeWriter to use.
This is the main service:
public class UCManagerService
: DomainService, IUCManagerService, ITransientDependency {
private readonly IUCInputReader _inputReader;
// This field can have 2 or 3 implementations.
private readonly IOutcomeWriter _outcomeWriter;
public UCManagerService(
IUCInputReader inputReader, IOutcomeWriter outcomeWriter) {
_inputReader = inputReader;
_outcomeWriter = outcomeWriter;
}
public async Task ExecuteAsync() {
// start processing the input and generate the output
var input = _inputReader.GetInput());
// do something
// ...
_outcomeWriter.Write(something);
}
}
The main service is registered in the AbpModule together with with IUCInputReader and the 2 implementations of IOutcomeWriter:
[DependsOn(
typeof(SwiftConverterDomainModule),
typeof(AbpAutofacModule) // <= use Autofac in some way (I don't know how)
)]
public class ProgramAppModule : AbpModule {
public override void ConfigureServices(ServiceConfigurationContext context) {
context.Services.AddTransient<IUCManagerService, UCManagerService>();
context.Services.AddTransient<IUCInputReader, UCInputReader>();
// 2 implementations of IOutcomeWriter
context.Services.AddTransient<IOutcomeWriter, OutcomeWriter1>();
context.Services.AddTransient<IOutcomeWriter, OutcomeWriter2>();
}
}
What I would like is to instantiate UCManagerService sometimes with OutcomeWriter1 and sometimes with OutcomeWriter2, according to some values in appsettings.json:
IList<JobSetting> jobsToSet = _configuration.GetSection("Jobs")
.Get<List<JobSetting>>();
foreach (JobSetting jobToSet in jobsToSet) {
// If jobsToSet.SomeValue == 'MyValue1' following line should have to
// require a IUCManagerService using OutcomeWriter1. If it is
// 'MyValue2' it'd use OutcomeWriter2, and so on:
var service = abpApplication.ServiceProvider.GetRequiredService<IUCManagerService>(); // ???
// do something else with service
// ...
}
Finally, if a tomorrow I add an OutcomeWriter3 I would just like to register it in ProgramAppModule.ConfigureServices(...) and of course use a different key in appsettings.json.
If I understand correctly, you need the IOutcomeWriter to differ based on the currently executed job. In other words, that means that you need to dynamically switch the writer based on its context.
The fact that it you need to change it dynamically, it means that is not a problem that can be solved solely using your DI configuration, because DI configurations are best kept static.
Instead, you need to mix and match a few concepts. First of all, you need a way to set the used job in the context. For instance:
// DI configuration
services.AddScoped<JobContext>();
// Execution of a job
using (var scope = abpApplication.ServiceProvider.CreateScope())
{
var context = scope.GetRequiredService<JobContext>();
context.CurrentJob = typeof(MyFirstJob);
var job = scope.GetRequiredService<MyFirstJob>();
var job.Execute();
}
In this example, JobContext is a class that holds the data that is used during the execution of a certain job. It is registered as Scoped to allow this data to be available for multiple classes within the same scope.
Now using this new JobContext, you can build an adapter for IOutcomeWriter that can forward the incoming call to the right implementation based on its injected JobContext. This might look as follows:
public class JobSpecificOutcomeWriter : IOutcomeWriter
{
private readonly JobContext context;
private readonly IList<JobSetting> settings;
private readonly IEnumerable<IOutcomeWriter> writers;
public JobSpecificOutcomeWriter(
JobContext context,
IList<JobSetting> settings,
IEnumerable<IOutcomeWriter> writers)
{
this.context = context;
this.settings = settings;
this.writers = writers;
}
// Implement all IOutcomeWriter methods by forwarding them to the
// CurrentWriter.
object IOutcomeWriter.SomeMethod(object a) =>
this.CurrentWriter.SomeMethod(a);
private IOutcomeWriter CurrentWriter
{
get
{
// TODO: Based on the current context and the settings,
// select the proper outcome writer from the writers list.
}
}
}
When JobSpecificOutcomeWriter is injected into UCManagerService (or any component for that matter), it transparently allows the proper writer to be used, without the consuming class from knowing about this.
The tricky part, actually, is to now configure your DI container correctly using JobSpecificOutcomeWriter. Depending on which DI Container you use, your mileage might vary and with the MS.DI Container, this is actually quite complicated.
services.AddTransient<IOutcomeWriter>(c =>
new JobSpecificOutcomeWriter(
context: c.GetRequiredService<JobContext>(),
settings: jobsToSet,
writers: new IOutcomeWriter[]
{
c.GetRequiredService<MyFirstJob>(),
c.GetRequiredService<MySecondJob>(),
c.GetRequiredService<MyThirdJob>(),
});
services.AddTransient<MyFirstJob>();
services.AddTransient<MySecondJob>();
services.AddTransient<MyThirdJob>();
I have many AOP libraries that use Castle DynamicProxy with Autofac DI container for logging, auditing, transaction control, etc.
I wonder if there is a way to declare interceptors using the default .NET Core DI container. It will be good to have this flexibility since many .NET Core projects don't use Autofac.
Yes, you can use DynamicProxy using Core DI. I've written up a blog post explaining it at http://codethug.com/2021/03/17/Caching-with-Attributes-in-DotNet-Core5/, but here is the code for it:
Create an attribute
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CacheAttribute : Attribute
{
public int Seconds { get; set; } = 30;
}
Create an interceptor (requires Castle.Core nuget package)
public class CacheInterceptor : IInterceptor
{
private IMemoryCache _memoryCache;
public CacheInterceptor(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
// Create a cache key using the name of the method and the values
// of its arguments so that if the same method is called with the
// same arguments in the future, we can find out if the results
// are cached or not
private static string GenerateCacheKey(string name,
object[] arguments)
{
if (arguments == null || arguments.Length == 0)
return name;
return name + "--" +
string.Join("--", arguments.Select(a =>
a == null ? "**NULL**" : a.ToString()).ToArray());
}
public void Intercept(IInvocation invocation)
{
var cacheAttribute = invocation.MethodInvocationTarget
.GetCustomAttributes(typeof(CacheAttribute), false)
.FirstOrDefault() as CacheAttribute;
// If the cache attribute is added ot this method, we
// need to intercept this call
if (cacheAttribute != null)
{
var cacheKey = GenerateCacheKey(invocation.Method.Name,
invocation.Arguments);
if (_memoryCache.TryGetValue(cacheKey, out object value))
{
// The results were already in the cache so return
// them from the cache instead of calling the
// underlying method
invocation.ReturnValue = value;
}
else
{
// Get the result the hard way by calling
// the underlying method
invocation.Proceed();
// Save the result in the cache
var options = new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
new System.TimeSpan(hours: 0, minutes: 0,
seconds: cacheAttribute.Seconds)
};
_memoryCache.Set(cacheKey, invocation.ReturnValue,
options);
}
}
else
{
// We don't need to cache the results,
// nothing to see here
invocation.Proceed();
}
}
}
Add an extension method to help register classes in DI:
public static void AddProxiedScoped<TInterface, TImplementation>
(this IServiceCollection services)
where TInterface : class
where TImplementation : class, TInterface
{
// This registers the underlying class
services.AddScoped<TImplementation>();
services.AddScoped(typeof(TInterface), serviceProvider =>
{
// Get an instance of the Castle Proxy Generator
var proxyGenerator = serviceProvider
.GetRequiredService<ProxyGenerator>();
// Have DI build out an instance of the class that has methods
// you want to cache (this is a normal instance of that class
// without caching added)
var actual = serviceProvider
.GetRequiredService<TImplementation>();
// Find all of the interceptors that have been registered,
// including our caching interceptor. (you might later add a
// logging interceptor, etc.)
var interceptors = serviceProvider
.GetServices<IInterceptor>().ToArray();
// Have Castle Proxy build out a proxy object that implements
// your interface, but adds a caching layer on top of the
// actual implementation of the class. This proxy object is
// what will then get injected into the class that has a
// dependency on TInterface
return proxyGenerator.CreateInterfaceProxyWithTarget(
typeof(TInterface), actual, interceptors);
});
}
Add these lines to ConfigureServices in Startup.cs
// Setup Interception
services.AddSingleton(new ProxyGenerator());
services.AddScoped<IInterceptor, CacheInterceptor>(
After that, if you want to use the cache interceptor, you need to do two things:
First, add the attribute to your method
[Cache(Seconds = 30)]
public async Task<IEnumerable<Person>> GetPeopleByLastName(string lastName)
{
return SomeLongRunningProcess(lastName);
}
Second, register the class in DI using the Proxy/Interception:
services.AddProxiedScoped<IPersonRepository, PersonRepository>();
Instead of the normal way without the Proxy/Interception:
services.AddScoped<IPersonRepository, PersonRepository>();
The base .NET Core container does not have any extra features like interceptors. The whole reason the DI container in .NET Core can be swapped out for something like Autofac is so you can move to a different container once you outgrow the default one.
I have a following problem. I register my components and initialize them in Unity like this (example is for a Console application):
public class SharePointBootstrapper : UnityBootstrapper
{
...
public object Initialize(Type type, object parameter) =>
Container.Resolve(type,
new DependencyOverride<IClientContext>(Container.Resolve<IClientContext>(parameter.ToString())),
new DependencyOverride<ITenantRepository>(Container.Resolve<ITenantRepository>(parameter.ToString())));
public void RegisterComponents()
{
Container
.RegisterType<IClientContext, SharePointOnlineClientContext>(SharePointClientContext.Online.ToString())
.RegisterType<IClientContext, SharePointOnPremiseClientContext>(SharePointClientContext.OnPremise.ToString())
.RegisterType<ITenantRepository, DocumentDbTenantRepository>(SharePointClientContext.Online.ToString())
.RegisterType<ITenantRepository, JsonTenantRepository>(SharePointClientContext.OnPremise.ToString());
}
}
public enum SharePointClientContext
{
Online,
OnPremise
}
class Program
{
static void Main(string[] args)
{
...
bootstrap.RegisterComponents();
var bla = bootstrap.Initialize(typeof(ISharePointManager), SharePointClientContext.Online);
}
}
So, I register my components in MVC, WCF, Console etc. once with RegisterComponents() and initialize them with Initialize().
My question is, if I want to initialize specific named registration at runtime, from e.g. user input, can it be done otherwise as the code presented (with InjectionFactory or similar)?
This code works fine, but I'm not happy with its implementation. I have a feeling that it could be written in RegisterComponents() instead of Initialize() so that it accepts a parameter of some type, but I don't know how to do it.
Or, is maybe my whole concept wrong? If so, what would you suggest? I need to resolve named registration from a parameter that is only known at runtime, regardless of the technology (MVC, WCF, Console, ...).
Thanks!
Instead of doing different registrations, I would do different resolves.
Let's say that you need to inject IClientContext, but you want different implementations depending on a runtime parameter.
I wrote a similiar answer here. Instead of injecting IClientContext, you could inject IClientContextFactory, which would be responsible for returning the correct IClientContext. It's called Strategy Pattern.
public interface IClientContextFactory
{
string Context { get; } // Add context to the interface.
}
public class SharePointOnlineClientContext : IClientContextFactory
{
public string Context
{
get
{
return SharePointClientContext.Online.ToString();
}
}
}
// Factory for resolving IClientContext.
public class ClientContextFactory : IClientContextFactory
{
public IEnumerable<IClientContext> _clientContexts;
public Factory(IClientContext[] clientContexts)
{
_clientContexts = clientContexts;
}
public IClientContext GetClientContext(string parameter)
{
IClientContext clientContext = _clientContexts.FirstOrDefault(x => x.Context == parameter);
return clientContext;
}
}
Register them all, just as you did. But instead of injecting IClientContext you inject IClientContextFactor.
There also another solution where you use a Func-factory. Look at option 3, in this answer. One may argue that this is a wrapper for the service locator-pattern, but I'll leave that discussion for another time.
public class ClientContextFactory : IClientContextFactory
{
private readonly Func<string, IClientContext> _createFunc;
public Factory(Func<string, IClientContext> createFunc)
{
_createFunc = createFunc;
}
public IClientContext CreateClientContext(string writesTo)
{
return _createFunc(writesTo);
}
}
And use named registrations:
container.RegisterType<IClientContext, SharePointOnlineClientContext>(SharePointClientContext.Online.ToString());
container.RegisterType<IClientContext, SharePointOnPremiseClientContext>(SharePointClientContext.OnPremise.ToString());
container.RegisterType<IFactory, Factory>(
new ContainerControlledLifetimeManager(), // Or any other lifetimemanager.
new InjectionConstructor(
new Func<string, IClientContext>(
context => container.Resolve<IClientContext>(context));
Usage:
public class MyService
{
public MyService(IClientContextFactory clientContextFactory)
{
_clientContextFactory = clientContextFactory;
}
public void DoStuff();
{
var myContext = SharePointClientContext.Online.ToString();
IClientContextclientContext = _clientContextFactory.CreateClientContext(myContext);
}
}
Using AutoFixture with the AutoFixture.AutoMoq package, I sometimes find tests that weren't configured to correctly test the thing they meant to test, but the problem was never discovered because of the default (Loose) Mock behavior:
public interface IService
{
bool IsSomethingTrue(int id);
}
void Main()
{
var fixture = new Fixture()
.Customize(new AutoMoqCustomization());
var service = fixture.Freeze<Mock<IService>>();
Console.WriteLine(service.Object.IsSomethingTrue(1)); // false
}
I'd like to make Mocks get created with Strict behavior, so we're forced to call Setup() for the methods we expect to be called. I can do this for each individual mock like this:
fixture.Customize<Mock<IService>>(c => c.FromFactory(() => new Mock<IService>(MockBehavior.Strict)));
But after combing through source code for AutoMoqCustomization() and the various ISpecimenBuilder and other implementations, I'm pretty lost as to the best way to just make all Mocks get initialized with strict behavior. The framework appears to be very flexible and extensible, so I'm sure there's a simple way to do this--I just can't figure out how.
There's no simple built-in feature that will enable you to do something like that, but it shouldn't be that hard to do.
Essentially, you'd need to change MockConstructorQuery so that it invokes the constructor that takes a MockBehavior value, and pass in MockBehavior.Strict.
Now, you can't change that behaviour in MockConstructorQuery, but that class is only some 9-10 lines of code, so you should be able to create a new class that implements IMethodQuery by using MockConstructorQuery as a starting point.
Likewise, you'll also need to create a custom ICustomization that does almost exactly the same as AutoMoqCustomization, with the only exception that it uses your custom IMethodQuery with strict mock configuration instead of MockConstructorQuery. That's another 7 lines of code you'll need to write.
All that said, in my experience, using strict mocks is a bad idea. It'll make your tests brittle, and you'll waste a lot of time mending 'broken' tests. I can only recommend that you don't do this, but now I've warned you; it's your foot.
For those interested, down below you can find #MarkSeemann's reply translated into code. I am pretty sure it does not cover all use cases and it was not heavily tested. But it should be a good starting point.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Moq;
using Ploeh.AutoFixture;
using Ploeh.AutoFixture.AutoMoq;
using Ploeh.AutoFixture.Kernel;
namespace ConsoleApplication1
{
public class StrictAutoMoqCustomization : ICustomization
{
public StrictAutoMoqCustomization() : this(new MockRelay()) { }
public StrictAutoMoqCustomization(ISpecimenBuilder relay)
{
// TODO Null check params
Relay = relay;
}
public ISpecimenBuilder Relay { get; }
public void Customize(IFixture fixture)
{
// TODO Null check params
fixture.Customizations.Add(new MockPostprocessor(new MethodInvoker(new StrictMockConstructorQuery())));
fixture.ResidueCollectors.Add(Relay);
}
}
public class StrictMockConstructorMethod : IMethod
{
private readonly ConstructorInfo ctor;
private readonly ParameterInfo[] paramInfos;
public StrictMockConstructorMethod(ConstructorInfo ctor, ParameterInfo[] paramInfos)
{
// TODO Null check params
this.ctor = ctor;
this.paramInfos = paramInfos;
}
public IEnumerable<ParameterInfo> Parameters => paramInfos;
public object Invoke(IEnumerable<object> parameters) => ctor.Invoke(parameters?.ToArray() ?? new object[] { });
}
public class StrictMockConstructorQuery : IMethodQuery
{
public IEnumerable<IMethod> SelectMethods(Type type)
{
if (!IsMock(type))
{
return Enumerable.Empty<IMethod>();
}
if (!GetMockedType(type).IsInterface && !IsDelegate(type))
{
return Enumerable.Empty<IMethod>();
}
var ctor = type.GetConstructor(new[] { typeof(MockBehavior) });
return new IMethod[]
{
new StrictMockConstructorMethod(ctor, ctor.GetParameters())
};
}
private static bool IsMock(Type type)
{
return type != null && type.IsGenericType && typeof(Mock<>).IsAssignableFrom(type.GetGenericTypeDefinition()) && !GetMockedType(type).IsGenericParameter;
}
private static Type GetMockedType(Type type)
{
return type.GetGenericArguments().Single();
}
internal static bool IsDelegate(Type type)
{
return typeof(MulticastDelegate).IsAssignableFrom(type.BaseType);
}
}
}
Usage
var fixture = new Fixture().Customize(new StrictAutoMoqCustomization());
I searched a lot and still couldn't find a solid solution for this. Suppose you have methods in your application. This methods use "System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration" to access some setting in the web.config. If you try to test these methods, your tests will fail because your test project doesn't have web.config.
What is the best way to solve this problem. For projects with simple config file, I usually use a method like this as facade method.
public class Config
{
public static String getKeyValue(String keyName)
{
if (keyName == String.Empty) return String.Empty;
String result = "";
System.Configuration.Configuration rootWebConfig1 =
System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(null);
if (rootWebConfig1.AppSettings.Settings.Count > 0)
{
System.Configuration.KeyValueConfigurationElement reportEngineKey =
rootWebConfig1.AppSettings.Settings[keyName];
if (reportEngineKey != null)
{
result = reportEngineKey.Value;
}
}
return result;
}
}
Every time I tried to set the path for OpenWebConfiguration( ), I got the error "The relative virtual path is not allowed"
To make that scenario more testable, I usually take the approach of making a "settings manager" of my own, and giving it an interface. So for example:
public interface IConfig
{
string GetSettingValue(string settingName);
}
Then I can have my "real" implementation:
public sealed class Config : IConfig
{
public string GetSettingValue(string settingName)
{
// your code from your getKeyValue() method would go here
}
}
Then my code that uses it would take in an instance of this (this is an example of the Dependency Inversion Principal):
public void DoStuff(IConfig configuration)
{
string someSetting = configuration.GetSettingValue("ThatThingINeed");
// use setting...
}
So now for my production code, I can call DoStuff and pass in an instance of Config.
When I need to test, I can use a mocking tool (Moq, JustMock, RhinoMocks, etc) to create a fake IConfig that returns a known value without hitting the actual .config file, or you can do it without a mocking framework by making your own mocks (and store them in your test project).
public class ConfigMock : IConfig
{
private Dictionary<string, string> settings;
public void SetSettingValue(string settingName, string value)
{
settings[settingName] = value;
}
public string GetSettingValue(string settingName)
{
return settings[settingName];
}
}
and
[Test]
public void SomeExampleTest()
{
var config = new ConfigMock();
config.SetSettingValue("MySetting", "SomeValue");
var underTest = new MyClass();
underTest.DoStuff(config);
}
The easiest way to do this is to use a mocking library such as moq. It takes a bit of time to figure it out, but once you do you can abstract away most of your plumbing to return the values you need for repeatable, consistent testing.