For each site in a single port, I need to connect to different server hosts.
While connecting for the first time, no issues. But while trying to register (RegisterDestinationConfiguration) with another connection pool name, it throws an exception. Destination configuration is already initialized for ConnectionPoolName1 so I can't register for ConnectionPoolName2.
function() {
if (SAPDestination == null) {
SAPDestination = SAPConnection(ApplicationSite);
RfcSessionManager.BeginContext(SAPDestination);
}
rfcTravelfunc = SAPDestination.Repository.CreateFunction("FunctionName");
}
private RfcDestination SAPConnection(SPSite ApplicationSite) {
RfcDestination SAPConnect = null;
try {
DestinationConfig objConfig = new DestinationConfig();
SAPConnect = objConfig.TryGetDestination(ConnectionPoolName); // If connection doesnt exist with this connection pool name returns null
if (SAPConnect == null) {
DestinationConfig configObj = new DestinationConfig();
DestinationConfig.ApplicationSite = ApplicationSite;
RfcDestinationManager.RegisterDestinationConfiguration(configObj); // Throws exception when trying to register for new connection pool name
SAPConnect = RfcDestinationManager.GetDestination(ConnectionPoolName);
}
}
catch(Exception ex) {
}
return SAPConnect;
}
RfcDestinationManager.RegisterDestinationConfiguration() is a global static method. You can only register once. It should be set in a static context (like a static class constructor) or you can use RfcDestinationManager.IsDestinationConfigurationRegistered() to check if already registered. The exception is thrown to prevent incorrect usage.
The registration object needs to implement SAP.Middleware.Connector.IDestinationConfiguration.
It has a method RfcConfigParameters GetParameters(string destinationName); This should return connection parameters for the requested destination.
You should use this way only if you need to look up the connection parameters from an external store. The simpler method is to store the connection parameters in the app.config/web.config (can store multiple) and not use RfcDestinationManager.RegisterDestinationConfiguration().
Taken from the SAP NCo Tutorial Code StepByStepClient.cs
The .Net Connector 3.0 introduces a new destination-oriented concept. Applications work with destination instances, which are configured per default in the application configuration file (app.config) or which can alternatively be defined by explicitly registering an IDestinationConfiguration object. A destination identifies the backend to which connections can be opened.
Related
THE GOAL
I'm trying to have a class library shared between an ASP.NET Core web app and other projects/solutions and the class should be able to interact with whatever database is being used by the calling process/environment.
THE PROBLEM
I have a class library that is throwing up a weird error when used in my ASP.NET Core 3.1 web app. The class library is actually shared between the front end (the website) and the backend app that takes care of some recurring, heavy load processes. I'm using EF Core with both front and back ends and the database is on Azure, not my local machine. Yet, when the web app tries to do some work I am getting the following error:
An attempt to attach an auto-named database for file C:...\bin\Debug\netcoreapp3.1\aspnetdb.mdf failed. A database with the same name exists, or specified file cannot be opened, or it is located on UNC share.
This doesn't make sense to me at all since the DB is on Azure. Also, calling the same exact method in the library using the backend app doesn't throw up this error. The connection string is stored in appsettings.json for the website and app.config for the backend.
This is block of code that is throwing the error, but again this is only happening on the ASP.NET Core project on the SaveChanges() call:
public static void AddLogEvent(int Severity, DateTime EventTime, string EventType, string User, string Message)
{
DBEntities context = new DBEntities();
DbSet<LogEvent> dbSet = context.Set<LogEvent>();
LogEvent NewRecord = new LogEvent();
NewRecord.Id = Guid.NewGuid();
NewRecord.Severity = Severity;
NewRecord.EventTime = EventTime;
NewRecord.EventType = EventType;
NewRecord.User = User;
NewRecord.Message = Message;
dbSet.Add(NewRecord);
context.SaveChanges();
}
Within DBEntities, I am overriding the OnConfiguring() method to ensure proper connection for whatever environment is making the call as such:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
bool FoundValidConnection = false;
if (ConfigurationManager.ConnectionStrings.Count > 0)
{
foreach (ConnectionStringSettings connstr in ConfigurationManager.ConnectionStrings)
{
if (FoundValidConnection == false)
{
if (string.Compare(connstr.Name.Trim().ToUpper(), "DefaultConnection".ToUpper()) == 0)
{
optionsBuilder.UseSqlServer(connstr.ConnectionString);
FoundValidConnection = true;
break;
}
}
}
foreach (ConnectionStringSettings connstr in ConfigurationManager.ConnectionStrings)
{
if (FoundValidConnection == false)
{
if (string.Compare(connstr.Name.Trim().ToUpper(), "DBEntities".ToUpper()) == 0)
{
optionsBuilder.UseSqlServer(connstr.ConnectionString);
FoundValidConnection = true;
break;
}
}
}
if (FoundValidConnection == false)
{
//if still haven't found one of the expected connection string names, then take whatever the first one is.
optionsBuilder.UseSqlServer(ConfigurationManager.ConnectionStrings[0].ConnectionString);
FoundValidConnection = true;
}
}
else
{
//nothing to do. there are no connection strings in the ConfigurationManager
}
}
}
Lastly, when I step through debugging on the website, I can see that the only connection string located in ConfigurationManager.ConnectionStrings is 1 with a name of LocalSqlServer and a connection string set to:
data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true
Which seems to be my local testing SQLExpress instance but is not used anywhere in the web app. All references or connection strings to the local testing database in SQLExpress have been removed so I am confused as to how this is showing up and the one in appsettings.json is being ignored. I also don't understand how optionsBuilder.IsConfigured is returning FALSE on the web app. I expected that context to already be configured.
I ended up changing the last if statement to the following:
if (FoundValidConnection == false)
{
try
{
//read from appsettings.json directly instead
string connString = new ConfigurationBuilder().SetBasePath(System.IO.Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build().GetSection("ConnectionStrings").GetSection("DefaultConnection").Value;
optionsBuilder.UseSqlServer(connString);
FoundValidConnection = true;
}
catch (System.Exception ex)
{
}
}
As Ivan noted in the comments above, ConfigurationManager from System.Configuration only works with app.config, which is XML based. ASP.NET Core utilizes appsettings.json which is, obviously, JSON based. So the solution was to modify the last check to be such that if the app.config checks fail and produce nothing then we assume the code is being called by something using appsettings.json and that one line uses ConfigurationBuilder to get the app's directory, build the configuration based on that file and get the expected connection by it's section and name.
References to Microsoft.Extensions.Configuration.FileExtensions and Microsoft.Extensions.Configuration.Json were required for SetBasePath() and AddJsonFile(), respectively.
The try/catch block is needed in the event there is some unforeseen error and the appsettings/json doesn't contain the expected section/name. Although, an error would eventually get thrown up somewhere when trying to interact with the database if nothing was set.
I have a XMS publish app, that is working, but it is including JMS headers as part of the message. My subscribe app is actually a python app, and I was wondering if it is possible to remove the JMS headers from the XMS app. I know it is possible in JMS, but is it possible in C# / XMS.
My C# code is fairly simple (with some of the details left out -
// Get an instance of factory.
factoryFactory = XMSFactoryFactory.GetInstance(XMSC.CT_WMQ);
// Create WMQ Connection Factory.
cf = factoryFactory.CreateConnectionFactory();
// Set the properties
cf.SetStringProperty(XMSC.WMQ_HOST_NAME, conn.host);
...
// Create connection.
connectionWMQ = cf.CreateConnection();
// Create session
sessionWMQ = connectionWMQ.CreateSession(false, AcknowledgeMode.AutoAcknowledge);
// Create destination
destination = sessionWMQ.CreateTopic(conn.topic_name);
// Create producer
producer = sessionWMQ.CreateProducer(destination);
// Start the connection to receive messages.
connectionWMQ.Start();
// Create a text message and send it.
textMessage = sessionWMQ.CreateTextMessage();
textMessage.Text = xmsJson.toJsonString();
producer.Send(textMessage);
In MQ /JMS I can use setTargetClient to remove the JMS headers -
private void setToNoJMSHeaders(Destination destination) {
try {
MQDestination mqDestination = (MQDestination) destination;
mqDestination.setTargetClient(WMQConstants.WMQ_CLIENT_NONJMS_MQ);
} catch (JMSException jmsex) {
logger.warning("Unable to set target destination to non JMS");
}
}
I was wondering if I can do the same to the topic destination in XMS
// Create destination
destination = sessionWMQ.CreateTopic(conn.topic_name);
// Configure the destination to Non-JMS
... ???
Yes, you should be able to do this
Try
// Create destination
destination = sessionWMQ.CreateTopic(conn.topic_name);
destination.SetIntProperty(XMSC.WMQ_TARGET_CLIENT, XMSC.WMQ_TARGET_DEST_MQ);
See: https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/com.ibm.mq.ref.dev.doc/prx_wmq_target_client.htm
I'm currently using in my project TuesPechkin version 2.1.1, and also TuesPechkin.Wkhtmltox.AnyCPU v0.12.4.1
This is some of my code:
byte[] result = null;
try
{
var globalSettings = CreateGlobalSettings(portraitMode);
var objectSettings = CreateObjectSettings(websiteUrl, urlParameters);
var document = new HtmlToPdfDocument
{
GlobalSettings = globalSettings
};
document.Objects.Add(objectSettings);
CreateEventLog.CreateInformationLog("Ready to convert PDF");
result = Converter.Convert(document);
CreateEventLog.CreateInformationLog(result == null
? "Conversion failed using the Pechkin library"
: "PDF conversion finished");
I run this code in 3 different environments:
On my local machine it runs fine and it generates the file in 3 seconds.
On one of my servers (let's call it Server A) it runs fine and it generates the file in 3 seconds.
On the other of my servers (let's call it Server B) it holds for 1min (for some reason I don't understand) during the Converter.Convert part, and after that minute it returns null.
Server A and Server B have the same setup (CPU, RAM, etc)
There's no peak increase on Server B during conversion.
Any suggestions/ideas?
I found what the issue is.
The URL I'm trying to convert is in a Presentation Layer, which is deployed in a separate server. Pechkin converter is in a Business Layer.
In Server A, I can access the URL from the Business Server.
In Server B, I cannot access the URL from the Business Server.
This is probably some firewall exception that needs to be created.
It would be nice though to have TuesPechkin, returning an error saying it cannot access the URL.
It is important to check how you get the convert, dispose issue may cause problem
Just check code form here
public static IConverter GetConverter()
{
lock (Locker)
{
if (converter != null)
{
return converter;
}
var tempFolderDeployment = new TempFolderDeployment();
var winAnyCpuEmbeddedDeployment = new WinAnyCPUEmbeddedDeployment(tempFolderDeployment);
IToolset toolSet;
if (HostingEnvironment.IsHosted)
{
toolSet = new RemotingToolset<PdfToolset>(winAnyCpuEmbeddedDeployment);
}
else
{
toolSet = new PdfToolset(winAnyCpuEmbeddedDeployment);
}
converter = new ThreadSafeConverter(toolSet);
}
return converter;
}
At my company we use the IBM DB2 for i5/OS .NET provider dll version 12.0.7.3. in a web application that makes database calls to the iSeries. We encountered a situation where a method which would create a connection then pass that connection to subroutines would seem to access the wrong data sometimes. An example case is as follows: Assume the server has just spun up. Client A calls the "GetContractPdf" and it succeeds. Client B then calls the same method and it fails. The server is restarted and Client B calls the "GetContractPdf" method which then succeeds. Client A calls it and it fails.
I pulled my hair out trying to figure this out but it appears that the code isn't disposing of or handling the database connections properly. It is reproducible in my development environment and when I do reproduce it the connection object shows the right library list for the client that I used to call it, but after inserting a test query that would yield data that could easily identify the client database being touched it was absolutely getting the wrong data. The databases in question are running on the same physical AS/400 machine with different library lists. A third client who has their own AS/400 is unaffected by this problem.
I have been changing any method that accepts a connection as an argument to create their own connection and hoped that it won't cause too big of a performance hit.
Here is the method that generates the connections:
public static iDB2Connection GetConnection(string ClientCode)
{
string decrypted = "";
try
{
decrypted = System.Configuration.ConfigurationManager.ConnectionStrings[ClientCode].ConnectionString;
}
catch (Exception)
{
throw new FaultException(string.Format("Client connection string not found for {0}.", ClientCode));
}
try
{
decrypted = EncryptDecrypt.Decrypt(decrypted);
}
catch (Exception)
{
throw new FaultException(string.Format("Error determining connection string for client {0}.", ClientCode));
}
try
{
return new iDB2Connection(decrypted);
}
catch (Exception)
{
throw new FaultException("Error accessing database.");
}
}
And here is some pseudocode that shows how it is used when the problem behavior arises:
public static ContractObject GetContract(clientCode,contractId){
using(iDb2Connection conn = GetConnection(clientCode)){
ContractObject contractObject = new ContractObject(){
Id = contractId
};
GetSomeData(contractObject,conn);
GetOtherData(contractObject,conn);
return contractObject;
}
}
public static void GetSomeData(ContractObject contract, iDb2Connection connection){
string commandText = "Select data from table where conditions";
using(iDb2Command cmd = new iDb2Command(commandText,connection)){
using(iDb2DataReader reader = cmd.ExecuteReader()){
if(reader.HasRows){
contract.Foo = reader["ColumnName"].ToString();
}
}
}
}
public static void GetOtherData(ContractObject contract, iDb2Connection connection){
string commandText = "Select data from table where conditions";
using(iDb2Command cmd = new iDb2Command(commandText,connection)){
using(iDb2DataReader reader = cmd.ExecuteReader()){
if(reader.HasRows){
contract.Bar = reader["ColumnName"].ToString();
}
}
}
}
If I change the methods up so that a new connection object is created for every query the behavior is eliminated.
As I see it there are two possible problems, either I have done something horribly wrong (which I certainly could have), or the IBM dll does not handle passing around connection objects well (which you would think is less likely but could happen). I'm sure there are other possibilities that I'm too inexperienced / code blind to see.
Do any of you have any ideas of what could be causing this behavior or questions that could lead us to figuring out what causes this behavior?
Thanks in advance for your time.
I have been attempting to start an instance of EC2 in C# without luck.
When passing in an instance id to start the instance I get an error that the instance cannot be found despite that I am passing in an instance ID that I have obtained from the object property.
Amazon made huge efforts to integrate its AWS Cloud .Net SDK To VS2008 & VS 2010
1 - Download and Install the AWS SDK msi
2 - Create an AWS Console project, enter your credentials (available from your AWS Console under your login name menu on the top right corner)
3 - Add the following code (see below images).
4 - Your're done. It's very straightforward. You can check the programmatic start/stop success by refreshing your AWS Console Screen.
AmazonEC2 ec2 = AWSClientFactory.CreateAmazonEC2Client();
//Start Your Instance
ec2.StartInstances(new StartInstancesRequest().WithInstanceId("i-00000000"));
//Stop it
ec2.StopInstances(new StopInstancesRequest().WithInstanceId("i-00000000"));
You just need to replace "i-00000000" by your instance Id (available in your AWS Management Console)
Hope this helps those googling this and stumbling upon this question (as I did myself) start off quickly. Following these simple steps via these wizards will spare you considerable headaches.
Try something like this with the AWSSDK to start new instances of an "image id":
RunInstancesResponse response = Client.RunInstances(new RunInstancesRequest()
.WithImageId(ami_id)
.WithInstanceType(instance_type)
.WithKeyName(YOUR_KEYPAIR_NAME)
.WithMinCount(1)
.WithMaxCount(max_number_of_instances)
.WithUserData(Convert.ToBase64String(Encoding.UTF8.GetBytes(bootScript.Replace("\r", ""))))
);
(Note: The .WithUserData() is optional and is used above to pass a short shell script.)
If the call is successful the response should contain a list of instances. You can use something like this to create a list of "instance ids":
if (response.IsSetRunInstancesResult() && response.RunInstancesResult.IsSetReservation() && response.RunInstancesResult.Reservation.IsSetRunningInstance())
{
List<string> instance_ids = new List<string>();
foreach (RunningInstance ri in response.RunInstancesResult.Reservation.RunningInstance)
{
instance_ids.Add(ri.InstanceId);
}
// do something with instance_ids
...
}
Be mindful that Amazon AWS instances exist only in one region. If your instance id i-12345 is in the EU-West-1 region, and you just make a new EC2Client and tell the client to start i-12345 it may well complain that it cannot find that instance, because the client started up in the us-east-1 region, which does not have i-12345 instance.
Your call that creates the client should specify the region, if it is not the default region (I've no idea which AWS region is default, so I specify every time):
AmazonEC2 ec2 = AWSClientFactory.CreateAmazonEC2Client(
new Amazon.EC2.AmazonEC2Config().WithServiceURL("https://eu-west-1.ec2.amazonaws.com")
);
Ok, this is the FULL, end-to-end instructions.
1. Install AWSSDK.Core and AWSSDK.EC2 using Nuget Package Manager.
2. Then copy this whole class to your project. AccessKey and Secret are obtained in AWS IAM. You will need to ensure the user you create has "AmazonEC2FullAccess" (You can probably use a lower-level permission policy, I am just lazy here :D). region is your AW S EC2 instance region. and Instance ID can be found in the EC2 dashboard list. Simple, works perfectly... You can also write extra code to manage the response object.
3. Be mindful if you are behind a proxy, you will have to configure it (I havent included code here).
public class AWSClass : IDisposable
{
Amazon.EC2.AmazonEC2Client _client;
public AWSClass(string region, string AccessKey, string Secret)
{
RegionEndpoint EndPoint = RegionEndpoint.GetBySystemName(region);
Amazon.Runtime.BasicAWSCredentials Credentials = new Amazon.Runtime.BasicAWSCredentials(AccessKey, Secret);
_client = new AmazonEC2Client(Credentials, EndPoint);
}
public void Dispose()
{
_client = null;
}
public void StopInstance(string InstanceID)
{
StopInstancesResponse response = _client.StopInstances(new StopInstancesRequest
{
InstanceIds = new List<string> {InstanceID }
});
//Can also do something with the response object too
}
public void StartInstance(string InstanceID)
{
StartInstancesResponse response = _client.StartInstances(new StartInstancesRequest
{
InstanceIds = new List<string> { InstanceID }
});
}
}
try this.
var startRequest = new StartInstancesRequest
{
InstanceIds = new List<string>() { instanceId }
};
bool isError = true;
StartInstancesResponse startInstancesResponse = null;
while (isError)
{
try
{
startInstancesResponse=amazonEc2client.StartInstances(startRequest);
isError = false;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + "\n" + ex.StackTrace);
isError = true;
}
}