I have a development version of Acumatica running locally, and a QA version of Acumatica in the cloud.
I wrote c# program integrating with Acumatica through SOAP. I created WSDL file for Acumatica instance running locally at http://localhost/AcumaticaERP. Now I need to make the program to connect with Acumatica production instance in the cloud. soapClient.Login method does not have Acumatica URL as a parameter.
How do I allow users to dynamically chose an instance of Acumatica to use from within my program?
I would first suggest to look into the REST API since generally that's the recommended integration API to use.
Regarding dynamically changing the endpoint (i.e. Acumatica instance), note that the DefaultSoapClient has a number of overloaded constructors.
There is one where you can specify the endpointConfigurationName (see below). This would mean that your URL should be in your web.config/appsettings of the client application as explained in more detail here: https://help-2020r1.acumatica.com/(W(1))/Help?ScreenId=ShowWiki&pageid=37613e5f-7a72-4dec-b5d9-2525951e99cf
public partial class DefaultSoapClient : System.ServiceModel.ClientBase<ConsoleApp2.ServiceReference1.DefaultSoap>, ConsoleApp2.ServiceReference1.DefaultSoap {
public DefaultSoapClient() {
}
public DefaultSoapClient(string endpointConfigurationName) :
base(endpointConfigurationName) {
}
public DefaultSoapClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}
public DefaultSoapClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}
public DefaultSoapClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress) {
}
In case using configuration files will not work for you, you can refer to this example whereby it is done programmatically:
https://asiablog.acumatica.com/2019/01/dynamic-api-endpoint-url.html
Related
ServiceStack uses a dialect of Funq (no support for metadata), where Kephas uses one of MEF/Autofac (requires metadata support). My question has two parts:
How to make ServiceStack and Kephas use one DI container, if this is possible?
Depending on the answer above: how to make ServiceStack services (like IClientCache) available to Kephas components, knowing that such services may not be annotated with [AppServiceContract]?
You can make ASP.NET and Kephas use one container by choosing to work with Autofac. However, as #mythz pointed out, you will need to provide the Autofac IoC Adapter to the ServiceStack. I don't think you will have any problems with ASP.NET in doing so, as Autofac is the first recommendation of the ASP.NET Core team.
For ASP.NET Core, reference the Kephas.AspNetCore package and inherit from the StartupBase class if you need to be all setup. However, if you need to be in control, have a look at https://github.com/kephas-software/kephas/blob/master/src/Kephas.AspNetCore/StartupBase.cs and write your own Startup class. Another resource that you might find useful is the Kephas.ServiceStack integration package.
Then, additionally to annotating service contracts and service implementations, Kephas allows you to provide service definitions by implementing the IAppServiceInfoProvider interface. These classes are automatically discovered, so this is pretty much everything you have to do.
public class ServiceStackAppServiceInfoProvider : IAppServiceInfoProvider
{
public IEnumerable<(Type contractType, IAppServiceInfo appServiceInfo)> GetAppServiceInfos(IList<Type> candidateTypes, ICompositionRegistrationContext registrationContext)
{
yield return (typeof(IUserAuthRepository),
new AppServiceInfo(
typeof(IUserAuthRepository),
AppServiceLifetime.Singleton));
yield return (typeof(ICacheClient),
new AppServiceInfo(
typeof(ICacheClient),
ctx => new MemoryCacheClient(),
AppServiceLifetime.Singleton));
}
}
Note in the above example that for IUserAuthRepository there is no implementation provided. This indicates Kephas to auto-discover the implementation in the types registered for composition. Alternatively, feel free to use an instance or a factory in the registration, if you need to be deterministic.
I've never heard of Kephas before, but if you're referring to this Kephas Framework on GitHub it says it uses ASP.NET Core in which case it's best if you get them to both use ASP.NET Core's IOC which you can do by either registering your dependencies in ConfigureServices in your App's Startup:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
//...
}
}
Or alternatively in ServiceStack's latest v5.6 release for Modular Startup change your ASP.NET Core Startup class to inherit from ModularStartup, e.g:
public class Startup : ModularStartup
{
public Startup(IConfiguration configuration) : base(configuration){}
public new void ConfigureServices(IServiceCollection services)
{
//...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//...
}
}
In which case you'll be able to Register ASP.NET Core dependencies in AppHost by registering them in your AppHost's Configure(IServiceCollection) where they can be resolved through both ASP.NET Core's IOC + ServiceStack's IOC, e.g:
public class AppHost : AppHostBase
{
public override void Configure(IServiceCollection services)
{
services.AddSingleton<IRedisClientsManager>(
new RedisManagerPool(Configuration.GetConnectionString("redis")));
}
public override void Configure(Container container)
{
var redisManager = container.Resolve<IRedisClientsManager>();
//...
}
}
I followed Alexander Duemont's blog, trying to implement a Java Spring Boot application that consumes Cloud Foundry Destination. The Destination has a custom OData V2 behind it, coming from an On-Premise ERP system. For local dev, when I perform the Maven build, the Integration-Tests module registers failure due to dependency injection
This is part of my Controller
#RestController
#RequestMapping("/resources")
public class ClassificationsController {
private static final Logger logger = CloudLoggerFactory.getLogger(ClassificationsController.class);
private final ClassificationService service;
public ClassificationsController(#Nonnull final ClassificationService service) {
this.service = service;
}
…..
}
The #Nonnull final ClassificationService Service causes org.springframework.beans.factory.UnsatisfiedDependencyException
I cannot use Spring stereotype annotations on generated Service classes (Fluent) to create Beans!
This question is more likely related to Spring Boot configuration.
I'm assuming ClassificationService is an interface and the implementing class exists in the same package.
Please make sure...
... to add the implementing class of ClassificationService to your component scan / test runtime. Feel free to share the integration test code to setup the test environment. Maybe the additional class reference is missing.
... to correctly annotate the respective Application class of your Spring Boot project. For example, assuming your ClassificationService resides in org.example.services.classification, while the rest of your application uses org.example.app. Your basic Application class would look like this, when following the Cloud SDK guide:
#SpringBootApplication
#ComponentScan({"com.sap.cloud.sdk", "org.example.services.classification", "org.example.app"})
#ServletComponentScan({"com.sap.cloud.sdk", "org.example.app"})
public class Application extends SpringBootServletInitializer
{
#Override
protected SpringApplicationBuilder configure( final SpringApplicationBuilder application )
{
return application.sources(Application.class);
}
public static void main( final String[] args )
{
SpringApplication.run(Application.class, args);
}
}
... to annotate the implementing class of ClassificationService with javax.inject.Named. In case you have multiple implementations of the same interface, make sure to give the not-used class a custom (unique) value for the #Named annotation.
... to look for exceptions (Class not found) in the application log during startup.
According to MT documentation each bus instance should have a different queue name.
Am I right to assume that following is correct configuration?
What wires the WebApi and Backend queue together?
Consider even more advanced scenario
When I design my backend pipeline to use message processing as well I can later slice it and let it use over the wire transport quite easilly. Question is, can I somehow configure MT so the Azure configured Bus relays the messages to the bus configured with local transport?
I'm going to answer this in the context of MassTransit v3, which supports both Azure Service Bus and RabbitMQ. If you are going to use Azure, I'd highly recommend v3 over the transport for v2.
First, about requests. They should be sent, not published. They are usually commands by nature, and not events. The only reason I see people publishing requests is that they don't have the endpoint address of the service. So knowing the endpoint helps, a lot.
Secondly, in your example, every WebAPI instance should have it's own queue to receive responses, since they are sent back to the requestor. With MT3, every IBus instance has a unique auto-delete queue that is setup for this very purpose, to handle responses from requests.
There is a sample, Sample-RequestResponse, in the MassTransit repository on GitHub, that shows how this is setup with RabbitMQ. It's about the same with Azure Service Bus.
The "fabric" that brings it all together is the virtual host (in RabbitMQ) or the namespace (in ASB). The connections between topics and queues determine how it all works together to form a logical bus.
If somebody is trying to us .NET Core + DI to register multiple busses:
Do NOT use the build in AddBus call
No matter what you do it will never register more than one Bus due
This is because internally it calls TryAddSingleton call
TryAddSingleton only adds a new instance to the DI container if there is no instance registered for the interface yet
Note:
No exception or error is thrown
The solution we use
Since the various interfaces necessary are not generic:
Created generic Wrappers around the built in interfaces
Created an interfaces (used a generic parameters) that uniquely identify each RegisteredBus
When a new Wrapper instance is created, we pass the instance of the built in interface into its constructor
The Wrapper then holds the instance of the built in internal interface in a public property Instance
Instead of injecting eg. IBus, we now inject IBus<MyRegisteredBus>
We then use the Instance property of the wrapper to get access to the built in interface instance and store it for later use (the wrapper plays no role after that)
We would love to not have to use some sort of wrapper with a weird Instance property, but short of the built in interface becoming generic or using something like DynamicProxies we could not come up with a more elegant solution.
Ideas / Feedback is very welcome.
Code
Generic AddBus call (otherwise 100% same signature as built in call):
public static void AddBus<TBusType>(this IServiceCollection services, Func<IServiceProvider, IBusControl> busFactory)
where TBusType : class, IBusType
{
IBusControl<TBusType> BusFactory(IServiceProvider serviceProvider)
{
return new BusControl<TBusType>(busFactory(serviceProvider));
}
services.AddSingleton<IBusControl<TBusType>>(BusFactory);
services.AddSingleton<IBus<TBusType>>(provider => new Bus<TBusType>(provider.GetRequiredService<IBusControl<TBusType>>().Instance));
}
The various interfaces / classes we created to make this happen:
// the only purpose of the interfaces derived from `IBusType` is to uniquely idnetify a registered Bus
public interface IBusType { }
public interface IHosted : IBusType { }
public interface ILocal : IBusType { }
public interface IBusTypeWrapper<TBusType, TInterface>
where TBusType : IBusType
{
public TInterface Instance { get; }
}
public class BusTypeWrapper<TBusType, TInterface> : IBusTypeWrapper<TBusType, TInterface>
where TBusType : IBusType
{
public TInterface Instance { get; }
public BusTypeWrapper(TInterface instance)
{
Instance = instance;
}
}
public interface IBusControl<T> : IBusTypeWrapper<T, IBusControl> where T : IBusType { }
public class BusControl<T> : BusTypeWrapper<T, IBusControl>, IBusControl<T> where T : IBusType
{
public BusControl(IBusControl instance) : base(instance) { }
}
public interface IBus<T> : IBusTypeWrapper<T, IBus> where T : IBusType { }
public class Bus<T> : BusTypeWrapper<T, IBus>, IBus<T> where T : IBusType
{
public Bus(IBus instance) : base(instance) { }
}
public interface ISendEndpointProvider<T> : IBusTypeWrapper<T, ISendEndpointProvider> where T : IBusType { }
public class SendEndpointProvider<T> : BusTypeWrapper<T, ISendEndpointProvider>, ISendEndpointProvider<T> where T : IBusType
{
public SendEndpointProvider(ISendEndpointProvider instance) : base(instance) { }
}
How to register generic ISendEndpointProvider:
services.AddSingleton<ISendEndpointProvider<ILocal>>(provider => new SendEndpointProvider<ILocal>(provider.GetRequiredService<IBusControl<ILocal>>().Instance));
UPDATE
To an IHosted service for each Bus type:
- Create a generic HostedService<BusType> service
- Inject the IBusControl<BusType> in the constructor
- and use the injected instance to start stop the specific bus
Afterwards register a IHostedServicefor each bus type.
services.AddSingleton<IHostedService, HostedService<ILocal>>(); services.AddSingleton<IHostedService, HostedService<IHosted>>();`
I have a Azure Mobile Services project. When running locally everything works fine, the Application_Start() method gets called which in turn calls my WebApiConfig.Register() method.
However, when published to a live Azure Mobile Services server the Application_Start() does not get called along with the WebApiConfig.Register().
In the servers log I have the following entry:
No bootstrapper found -- using default bootstrapper. A bootstrapper can be specified in one of two ways: Either by defining a public, static class with name 'WebApiConfig' having a public parameter-less member called 'Register', or using the 'IBootstrapper' attribute to define a public class with a default constructor.
Why is Azure Mobile Services not picking up my BootStrapping WebApiConfig?
public static class WebApiConfig
{
public static void Register()
{
Trace.TraceInformation("Hello from WebApiConfig Register().");
// Use this class to set configuration options for your mobile service
ConfigOptions options = new ConfigOptions();
// Use this class to set WebAPI configuration options
HttpConfiguration config = ServiceConfig.Initialize(new ConfigBuilder(options));
// To display errors in the browser during development, uncomment the following
// line. Comment it out again when you deploy your service for production use.
// config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
}
}
public class WebApiApplication : System.Web.HttpApplication
{
public WebApiApplication()
{
Trace.TraceInformation("Hello from WebApiApplication ctor!");
}
protected void Application_Start()
{
Trace.TraceInformation("Hello from Application_Start()");
//RouteConfig.RegisterRoutes(RouteTable.Routes);
WebApiConfig.Register();
var dataContext = new DataContext();
dataContext.Database.Initialize(false);
}
}
Help is much appreciated!
That is bizarre... It really looks like you got it right. After working with .net backend azure mobile service for few weeks, I might suggest just maybe restart the service in portal and republish. I have hit some weird unexplained stuff just like you are and somehow fix like that.
I am doing some stuff in Service Stack self host in windows service. The link gave me some hint. But in the code, what is StarterTemplateAppListenerHost then?
It is a class which extends AppHostHttpListenerBase (Source here) which is used to provide the http listener and application configuration.
public class StarterTemplateAppListenerHost : AppHostHttpListenerBase
{
static readonly IAppSettings AppSettings = new AppSettings();
public StarterTemplateAppListenerHost()
: base(AppSettings.GetString("ServiceName") ?? "StarterTemplate HttpListener", typeof(HelloService).Assembly) { }
public override void Configure(Funq.Container container)
{
container.Register(new TodoRepository());
}
}
This is demonstrated also in the official documentation here.
I just wonder why the link doesn't have OnStart() etc
The example has two different compilation modes. When it's run in debug, it will not run as a service, and solely uses StarterTemplateAppListenerHost.
When it is run in release mode then it will create a service around the instance of StarterTemplateAppListenerHost. The WinService class provides the OnStart and OnStop methods which are expected of Windows Services by extending System.ServiceProcess.ServiceBase.
So to get it running as a Windows Service you will need to include these 3 files:
Program.cs
WinService.cs
StarterTemplateAppListenerHost.cs