Catel Auto Register Attributes with multiple instances - catel

we are developing a GUI Plug-In Framework using Catel.MVVM.
Single Plugins should be loaded dynamically using the "ServiceLocatorRegistration" Attribute.
Example:
[ServiceLocatorRegistration(typeof(IInitializable), ServiceLocatorRegistrationMode.SingletonInstantiateWhenRequired, "SamplePlugin")]
In our bootstrapper we load all Plugin assemblies into the default AppDomain:
Catel.Windows.Controls.MVVMProviders.Logic.UserControlLogic.DefaultSkipSearchingForInfoBarMessageControlValue = true;
Catel.Windows.Controls.MVVMProviders.Logic.UserControlLogic.DefaultCreateWarningAndErrorValidatorForViewModelValue = false;
IoCConfiguration.DefaultServiceLocator.AutoRegisterTypesViaAttributes = true;
IoCConfiguration.DefaultServiceLocator.CanResolveNonAbstractTypesWithoutRegistration = true;
foreach (
var file in
BaseDirectory.GetFiles("*.dll", SearchOption.AllDirectories)
.Where(f => IsNetAssembly(f.FullName))
.Where(f => !f.FullName.EndsWith("resources.dll"))
.AsParallel())
{
try
{
var asm = Assembly.ReflectionOnlyLoadFrom(file.FullName);
}
catch { }
}
Then we try to initialize them by calling
var initializables = ServiceLocator.Default.ResolveTypes();
foreach(var initializable in initializables)
initializable.Initialize();
But even if we have the plugin assemblies loaded in the AppDomain, we dont get all Classes with the ServiceLocatorRegistration attribute.
Is there any was to resolve all classes that have the example attribute set as above?
Thanks in advance!

The problem is probably because the assemblies containing the types that use the registration are not loaded into the AppDomain yet. There are a few things you can consider:
1) Use AppDomainExtensions.PreloadAssemblies
2) Use the type somehow (like Console.WriteLine(typeof(TypeFromAssembly).FullName))
I wouldn't recommend the second one because it goes against your plug-in architecture.

Resolved this issue myself. Mistake was probably the linevar asm = Assembly.ReflectionOnlyLoadFrom(file.FullName);
After replacing this line withAppDomain.CurrentDomain.LoadAssemblyIntoAppDomain(file.FullName); everything works as expected.
Thanks Geert for pointing to the right direction!

Related

AutoMapper.Map overload with IMappingOperations missing from ResolutionContext.Mapper

I have a TypeConverter in which I'm using context.Mapper.Map() to map two subproperties.
The properties are the same Type and use the same (another) TypeConverter. However in one the properties I need to pass some IMappingOperationsOptions.
It looks like this (simplified):
public class MyTypeConverter : ITypeConverter<A, B>
{
public B Convert(A, B, ResolutionContext context)
{
var subProp1 = context.Mapper.Map<C>(B.SomeProp);
var subProp2 = context.Mapper.Map<C>(B.SomeOtherProp, ops => ops.Items["someOption"] = "someValue");
return new B
{
SubProp1 = subProp1,
SubProp2 = subProp2
};
}
}
This was working fine in AutoMapper 8.0.0 but I'm upgrading to AutoMapper 10.1.1 (last version with .NET framework support).
In this newer version of AutoMapper the overload method to pass IMappingOperationsOptions does not exist anymore.
I could (theoretically) solve this by injecting IMapper in the constructor of the TypeResolver and use that instead of the ResolutionContext's Mapper but that doesn't feel right.
At the moment I solved the issue by temporarily updating the ResolutionContext options, but that also doesn't really feel right.
var subProp1 = context.Mapper.Map<C>(B.SomeProp);
context.Options.Items["someOption"] = "someValue";
var subProp2 = context.Mapper.Map<C>(B.SomeOtherProp);
context.Options.Remove("someOption");
Casting ((IMapper)context.Mapper).Map() crashes so that's not an option either. Is there a more elegant way to achieve this?

Create shared NUnit tests for plugins

I have plugins and i need to test, that any plugin fits to some specification. One of these cases is to check whether some interface exists in assembly (need to reflect from assembly).
I'd like to create some console application which will take plugin as an argument and check it.
This application will contain a set of tests, that will be configured by a passed argument. And the test runner which will produce xml report to output.
Is there better solution?
Update.
In my console application i call:
static int Main(string[] args)
{
CoreExtensions.Host.InitializeService();
var runner = new SimpleTestRunner();
var testPackage = new TestPackage(Assembly.GetExecutingAssembly().FullName);
string loc = Assembly.GetExecutingAssembly().Location;
testPackage.Assemblies.Add(loc);
if (runner.Load(testPackage))
{
var result = runner.Run(new NullListener(), new AllTestsFilter(), false, LoggingThreshold.Off);
var buffer = new StringBuilder();
new XmlResultWriter(new StringWriter(buffer)).SaveTestResult(result);
Console.Write(buffer.ToString());
return result.IsSuccess
? 0
: -1;
}
return -1;
}
In this soultion i have tests, but i need to pass arguments from command line to this tests through runner..
Probably you can use the TestCaseSource attribute: http://nunit.org/index.php?p=testCaseSource&r=2.6.3
Inside the test case source property you can enumerate the assemblies to test: NUnit will take care to generate a parametric test for each value.
Regarding the command line execution, you can use nunit-console.exe. You can get it here: http://www.nuget.org/packages/NUnit.Runners/
Hope it helps.
solved this problem by creation simple console application without NUnit.. Just return code -1/0

Trying "Messaging via attribute" from the documentation

I'm trying to get the hung of catel but have a problem.
Trying "Messaging via attribute" gets an compile error.
'Catel.MVVM.ViewModelBase.GetService(object)' is obsolete: 'GetService is no longer >recommended. It is better to inject all dependencies (which the TypeFactory fully supports) >Will be removed in version 4.0.0.'
private void OnCmdExecute()
{
var mediator = GetService<IMessageMediator>();
mediator.SendMessage("Test Value");
}
[MessageRecipient]
private void ShowMessage(string value)
{
var messageService = GetService<IMessageService>();
messageService.Show(value);
}
I'm using 3.9.
A hint and a code snippet whould be good help.
Thanks for your attention.
The GetService is marked obsolete. You have 2 options:
1) If you are using a view model, simply let the services be injected in the constructor:
private readonly IMessageMediator _messageMediator;
private readonly IMessageService _messageService;
public MyViewModel(IMessageMediator messageMediator, IMessageService messageService)
{
Argument.IsNotNull(() => messageMediator);
Argument.IsNotNull(() => messageService);
_messageMediator = messageMediator;
_messageService= messageService;
}
2) Use the GetDependencyResolver extension method:
var dependencyResolver = this.GetDependencyResolver();
var messageMediator = dependencyResolver.Resolve<IMessageMediator>();
Solution 1 is the recommended way.
Thanks for your answer.
I also found a good example in the "Catel.Examples" solution, link to download

Extend the default behaviour of AutoMapper

I want to customise the way AutoMapper converts my types without losing the features already implemented by AutoMapper.
I could create a custom ITypeConverter instance but I can't see how to invoke the default behaviour.
Mapper.CreateMap<MyDomainObject, MyDto>
.ConvertUsing<MyTypeConverter>();
...
public class MyTypeConverter : TypeConverter<MyDomainObject, MyDto>
{
public MyDto ConvertCore(MyDomainObject source)
{
var result = // Do the default mapping.
// do my custom logic
return result
}
}
If I try to call var result = Mapper.Map<MyDto>(source) it gets into an infinite loop. I effectively want AutoMapper to do everything it normally would assuming there was no TypeConverter defined.
Any help greatly appreciated.
If you only want to customise some values on the destination object, then you're better off with a Custom Value Resolver - TypeConverters are designed to handle the whole conversion.
The doc page listed above will have enough to get you started: when you have implemented the CustomResolver you apply it like this, and AutoMapper will do the default mapping for the other properties:
Mapper.CreateMap<MyDomainObject, MyDto>()
.ForMember(dest => dest.TargetProperty,
opt => opt.ResolveUsing<CustomResolver>());

using a common layout for several modules in Yii

I am using Yii framework in my web project. now, I have several modules and I want to use only one layout for all modules. I have used following codes for determining the layout for every controller/action in each module:
$this->layoutPath = Yii::getPathOfAlias('application.views.layouts');
$this->layout = '//layouts/myLayout';
Is there any other solution to do this by using same code in init() function of each module?
in other word, I have to write the above 2-line code in each action and i think it's not good and i want to reduce my number of lines of codes. for example as follows:
class StaffModule extends CWebModule
{
public $layout;
public $layoutPath;
public function init()
{
$this->layoutPath = Yii::getPathOfAlias('application.views.layouts');
$this->layout = '//layouts/myLayout';
$this->setImport(array(
'staff.models.*',
'staff.components.*',
));
}
}
but it doesn't work. Help me please.
Just use
$this->layout='//layouts/myLayout';
without
$this->layoutPath = Yii::getPathOfAlias('application.views.layouts');
because // mean you specific absolute path from root
The approach you are having in init function is in the right direction..I think the problem could be.. as you are defining layoutPath you shouldn't have //layouts..
$this->layoutPath = Yii::getPathOfAlias('application.views.layouts');
$this->layout = 'myLayout';
and you don't need these:
public $layout;
public $layoutPath;
I've answered similar question on
using common layout for several modules
The solution is set the layout on beforeControllerAction in your module.
It should work.

Resources