- will
- Nombre de messages : 9
Date d'inscription : 29/09/2021
Fiche d'Entreprise
Nom de l'entreprise:
Acomba SDK en conjonction avec DI, objet non enregistré ?
Mar 12 Oct 2021 - 9:31
Je développe un service windows qui fait le pont avec acomba. Le service surveille un dossier pour des fichiers transactionnels puis les traite lorsqu'il en trouve.
Comme tout service windows, le point d'entrée est la classe Program, en C#. C'est donc là que j'initialise, en terme DI, mes services. Afin de sépararer les responsabilités, je me suis créé une classe que j'ai nommé NinjectCompositionRoot. Au sein de celle-ci j'utilise simplement l'outil Ninject et les liaisons de type par convention (convention binding).
Voici un exemple :
Comme le sdk acomba est constitué d'objects com, les classes ne peuvent être instancié directement, car celles-ci ne peuvent être intégrées à l'assembly qui fait référence au sdk. Par ailleurs, des interfaces d'interpolation sont créées pour pouvoir instancier les objets et les utiliser.
Ce qui permet alors de faire :
Mais comme j'utilise l'injection de dépendances, j'essaie donc d'instruire Ninject sur comment instancier ces objets par convention comme suit :
En exécution, j'obtiens l'exception :
J'ai pourtant enregistré la dll acosdk.dll à l'aide de regsvr32, et si j'instancie en faisant un new, ça fonctionne.
Je reconnais que c'est pas directement en lien avec le sdk d'acomba et plutôt avec les objets COM en général. Et si qqn pouvait m'aider, je l'apprécierais vraiment ! Mon but serait que Ninject puisse instancier toutes les dépendances aux objets COM au fur et à mesure que je les utilise de sorte que je n'aie plus à rejouer avec le composition root.
Comme tout service windows, le point d'entrée est la classe Program, en C#. C'est donc là que j'initialise, en terme DI, mes services. Afin de sépararer les responsabilités, je me suis créé une classe que j'ai nommé NinjectCompositionRoot. Au sein de celle-ci j'utilise simplement l'outil Ninject et les liaisons de type par convention (convention binding).
Voici un exemple :
- Code:
public class NinjectCompositionRoot {
private readonly IKernel _services;
private NinjectCompositionRoot()
=> _services = new StandardKernel(new AutoMapperModule(), new AcombaBindingModule());
public IKernel RegisterServices() {
_services.Bind(services => services
.FromThisAssembly()
.SelectAllClasses()
.BindAllInterfaces());
return _services;
}
}
Comme le sdk acomba est constitué d'objects com, les classes ne peuvent être instancié directement, car celles-ci ne peuvent être intégrées à l'assembly qui fait référence au sdk. Par ailleurs, des interfaces d'interpolation sont créées pour pouvoir instancier les objets et les utiliser.
- Code:
[ComImport]
[TypeLibType(TypeLibTypeFlags.FDual | TypeLibTypeFlags.FDispatchable)]
[Guid("00000114-0000-000F-1000-000AC0BA1001")]
public interface IAcoSDKX { ... }
[ComImport]
[Guid("00000114-0000-000F-1000-000AC0BA1001")]
[CoClass(typeof(AcoSDKXClass))]
public interface AcoSDKX { ... }
Ce qui permet alors de faire :
- Code:
IAcoSDKX sdk = new AcoSDKX(); // Et ce même si les deux types ici présents sont des interfaces.
Mais comme j'utilise l'injection de dépendances, j'essaie donc d'instruire Ninject sur comment instancier ces objets par convention comme suit :
- Code:
public class AcombaBindingModule : NinjectModule {
public override void Load()
=> AppDomain.CurrentDomain
.GetAssemblies()
.Single(a => a.GetTypes().Contains(typeof(AcoSDKX)))
.GetExportedTypes()
.Where(t => t.IsInterface && !t.Name.StartsWith("I"))
.ToList()
.ForEach(type => {
var bindingType = type.GetInterfaces().Single(i => i.Name.Contains(type.Name));
var typeFromCLSID = Type.GetTypeFromCLSID(type.GUID);
Kernel.Bind(bindingType).ToMethod(_ => Activator.CreateInstance(typeFromCLSID));
});
}
En exécution, j'obtiens l'exception :
System.Runtime.InteropServices.COMException: 'Retrieving the COM class factory for component with CLSID {00000114-0000-000F-1000-000AC0BA1001} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).'
J'ai pourtant enregistré la dll acosdk.dll à l'aide de regsvr32, et si j'instancie en faisant un new, ça fonctionne.
- Code:
public class AcombaBindingModule : NinjectModule {
public override Load() {
Kernel.Bind<IAcoSDKX>().ToMethod(_ => new AcoSDKX());
Kernel.Bind<IAcombaX>().ToMethod(_ => new AcombaX());
Kernel.Bind<IUser>().ToMethod(_ => new User());
}
}
Je reconnais que c'est pas directement en lien avec le sdk d'acomba et plutôt avec les objets COM en général. Et si qqn pouvait m'aider, je l'apprécierais vraiment ! Mon but serait que Ninject puisse instancier toutes les dépendances aux objets COM au fur et à mesure que je les utilise de sorte que je n'aie plus à rejouer avec le composition root.
- will
- Nombre de messages : 9
Date d'inscription : 29/09/2021
Fiche d'Entreprise
Nom de l'entreprise:
Re: Acomba SDK en conjonction avec DI, objet non enregistré ?
Mar 19 Oct 2021 - 13:28
J'ai abdiqué et j'ai décidé de créer un module Acomba où je "bind" tous les types utilisés. Cela nécessite que j'oublie pas de bien ajouter les types qui seraient nouvellement utilisés, autrement une exception sera levée lors de l'exécution.
Simplement ne pas oublier d'ajouter le module au noyau de Ninject. Pour ce faire, j'ai décidé d'y aller avec une extension.
Et j'appelle donc cette méthode lors de l'enregistrement des services.
- Code:
public class AcombaModule : NinjectModule {
public override void Load() {
Kernel.Bind<IAcoSDKX>().ToMethod(_ => new AcoSDKX()).InSingletonScope();
Kernel.Bind<IAcombaX>().ToMethod(_ => new AcombaX()).InSingletonScope();
Kernel.Bind<IUser>().ToMethod(_ => new User()).InSingletonScope();
Kernel.Bind<ISupplier>().ToMethod(_ => new Supplier());
Kernel.Bind<ICharter>().ToMethod(_ => new Charter());
Kernel.Bind<IPayables>().ToMethod(_ => new Payables());
Kernel.Bind<ITransAP>().ToMethod(_ => new TransAP());
Kernel.Bind<IPaymentAP>().ToMethod(_ => new PaymentAP());
Kernel.Bind<IInvoiceAP>().ToMethod(_ => new InvoiceAP());
Kernel.Bind<IPaymentAPLine>().ToMethod(_ => new PaymentAPLine());
}
}
Simplement ne pas oublier d'ajouter le module au noyau de Ninject. Pour ce faire, j'ai décidé d'y aller avec une extension.
- Code:
namespace Ninject {
public static class KernelExtensions {
public static IKernel BindAcomba(this IKernel kernel) {
kernel.Load<NinjectCompositionRoot.Modules.AcombaModule>();
return kernel;
}
}
}
Et j'appelle donc cette méthode lors de l'enregistrement des services.
- Code:
public IKernel RegisterServices() {
_services.BindMediatR();
_services.BindAutoMapper();
_services.BindAcomba();
_services.Bind(services => services.FromThisAssembly().SelectAllClasses().BindAllInterfaces());
return _services;
}
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum