Shared resources while using Task.Factory - c#

Here is the code whose responsibility is to connect to the server and get some details.
public class MigrationManager
{
private string referenceServerId;
public void MigrateAnalyzer(string serverid)
{
Gw _Gw = new Gw() // library reference
_Gw.onConnectSucceeded += Handle_OnConnectSucceeded;
_Gw.onConnectFailed += Handle_OnConnectFailed;
ConnectToServer(prxHA, serverid);
}
private void ConnectToServer(string sid)
{
workstationIDForReference = anayzer;
string credentials = GWServiceCredentials("somesecret");
referenceServerId = sid;
_Gw.connectToServer("SSL", "device1-mydomain.net", credentials, referenceServerId);
}
}
This connection operation can end up in two scenarios.
Connection success
Connection failure
These events handled as shown below
private void Handle_OnConnectFailed(int hr)
{
string msg = $"{referenceServerId}";
Console.WriteLine("Connecting to server {0} is failed", msg);
}
private void Handle_OnConnectSucceeded()
{
Console.WriteLine("Connecting to server is success");
}
This works well. Now I am changing it to support multithreading. Because there are multiple requests to connect to the server.
List<string> requestCollection = new List<string>();
requestCollection.Add("I3-1");
requestCollection.Add("I3-2");
requestCollection.Add("I3-3");
var taskList = new List<Task>();
foreach (var serverid in requestCollection )
{
MigrationManager manager = new MigrationManager();
var task = Task.Factory.StartNew(() => manager.MigrateAnalyzer(serverid.ToString()));
}
Task.WaitAll(taskList.ToArray());
I see below Output
Console.WriteLine("Connecting to server I3-1 is failed");
Console.WriteLine("Connecting to server I3-2 is failed");
Console.WriteLine("Connecting to server I3-3 is failed");
Output looks good!
But, I think COM will use shared resources. so how do I use "lock"?

Related

MqttNet version 4.1.3.563 Basic example

Following this example I have now therefore been required to update the MQTT.NET from version 3 (that works thanks the provided help) to version 4.
A very basic set of capabilities would be enough:
Connect to an adress with a timeout
Check if the connection has gone well
Receive messages
check disconnection
that was extremely easy in version 3
MqttClientOptionsBuilder builder = new MqttClientOptionsBuilder()
.WithClientId("IoApp" + HelperN.MQTT.GetClientID(true))
.WithTcpServer("localhost", 1883);
ManagedMqttClientOptions options = new ManagedMqttClientOptionsBuilder()
.WithAutoReconnectDelay(TimeSpan.FromSeconds(60))
.WithClientOptions(builder.Build())
.Build();
mqttClient = new MqttFactory().CreateManagedMqttClient();
mqttClient.ConnectedHandler = new MqttClientConnectedHandlerDelegate(OnConnected);
mqttClient.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(OnDisconnected);
mqttClient.ConnectingFailedHandler = new ConnectingFailedHandlerDelegate(OnConnectingFailed);
mqttClient.SubscribeAsync(...);
mqttClient.SubscribeAsync(...);
mqttClient.StartAsync(options).GetAwaiter().GetResult();
mqttClient.UseApplicationMessageReceivedHandler(args => { OnMessageReceived(args); });
but when it comes to version 4 if I have to relay on those examples I have problems.
Let's start from the connection
public static async Task Connect_Client_Timeout()
{
/*
* This sample creates a simple MQTT client and connects to an invalid broker using a timeout.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
var mqttFactory = new MqttFactory();
strError = String.Empty;
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("aaaa127.0.0.1",1883).Build();
try
{
using (var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
{
await mqttClient.ConnectAsync(mqttClientOptions, timeoutToken.Token);
}
}
catch (OperationCanceledException exc)
{
strError = "Connect_Client_Timeout exc:" + exc.Message;
}
}
}
And I call this task from the main awaiting the result.
var connectTask = Connect_Client_Timeout();
connectTask.Wait();<-----never ends
Since I put a wrong address "aaaa127.0.0.1" I expect a failure after 5 seconds. But the connectTask.Wait never end. But even if I put the right address "127.0.0.1" it never exits.
So perhaps the error stands in the connectTask.Wait();.
Thanks
The solution is here
In short you have to do this:
static async Task Connect()
{
IManagedMqttClient _mqttClient = new MqttFactory().CreateManagedMqttClient();
// Create client options object
MqttClientOptionsBuilder builder = new MqttClientOptionsBuilder()
.WithClientId("behroozbc")
.WithTcpServer("localhost");
ManagedMqttClientOptions options = new ManagedMqttClientOptionsBuilder()
.WithAutoReconnectDelay(TimeSpan.FromSeconds(60))
.WithClientOptions(builder.Build())
.Build();
// Set up handlers
_mqttClient.ConnectedAsync += _mqttClient_ConnectedAsync;
_mqttClient.DisconnectedAsync += _mqttClient_DisconnectedAsync;
_mqttClient.ConnectingFailedAsync += _mqttClient_ConnectingFailedAsync;
// Connect to the broker
await _mqttClient.StartAsync(options);
// Send a new message to the broker every second
while (true)
{
string json = JsonSerializer.Serialize(new { message = "Hi Mqtt", sent = DateTime.UtcNow });
await _mqttClient.EnqueueAsync("behroozbc.ir/topic/json", json);
await Task.Delay(TimeSpan.FromSeconds(1));
}
Task _mqttClient_ConnectedAsync(MqttClientConnectedEventArgs arg)
{
Console.WriteLine("Connected");
return Task.CompletedTask;
};
Task _mqttClient_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
{
Console.WriteLine("Disconnected");
return Task.CompletedTask;
};
Task _mqttClient_ConnectingFailedAsync(ConnectingFailedEventArgs arg)
{
Console.WriteLine("Connection failed check network or broker!");
return Task.CompletedTask;
}
}
and then just call Connect() and rely on the subscribed examples

Consume EventArgs from CouchDB

i am struggling with consuming the data i get from my CouchDB database.
I am trying to consume new data that comes to the specific view.
CouchDB offers an option for feed=continous, but i tested it and dont get any data, same in postman.
But if i change it to feed=eventsource i can see the changes in the console. But i dont know how to handle the events.
I opened a method with the right connection, but im stuck now, any help would be great.
public async Task ObserveDbAndTrigger()
{
var url = "http://localhost:5984/MyDB/_changes?feed=eventsource&filter=_view&view=MyView&include_docs=true&attachments=true&heartbeat=1000&since=0";
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes($"user:password" + $"")));
var request = new HttpRequestMessage(HttpMethod.Get, url);
// handle the incoming events and work with the incoming data
}
}
Any suggestions ?
Clearly there's work to be done. Normally I shy away from answering such questions as posed because it seems like a code service request, but I believe this answer may benefit others beyond the OP.
Here is an extremely naïve bit of code meant to illustrate event delegation and the simplicity of communicating with CouchDB over TCP.
Ultimately this demonstrates the publish/subscribe pattern, which is a reasonable fit. I tested this against CouchDB 2.3 on Windows. The code is hardwired to localhost:5984 because whatever.
class NaiveChangeWatcher
{
static void Main(string[] args)
{
if (args.Length >= 4)
{
// set up server info.
string db = args[0];
string auth = "Basic " + Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(String.Join(":", args[1], args[2])));
string query = db + "/_changes?feed=continuous&since=0&heartbeat=" + args[3];
// init the publisher
ChangesPublisher pub = new ChangesPublisher();
// let's subscribe to the OnChange event which writes event data to the console.
pub.OnChange += (sender, e) => Console.WriteLine(e.Value);
pub.OnException += (sender, e) => Console.WriteLine(e.Value.ToString() + "\r\n\r\nPress a key to exit.");
//// start publishing.
Task.Run(async () =>
{
await pub.Begin("localhost", 5984, query, auth, int.Parse(args[3]));
});
// Press a key when bored of it all
Console.ReadKey();
// stop the publisher gracefully
pub.Stop = true;
}
else
{
Console.WriteLine("usage: NaiveChangeWatcher db_name username password timeout_millis");
}
}
//
// The ChangesPublisher notifies subscribers of new data from the changes feed
// via the ChangeEvent. The publisher will trigger an OnException event in the
// event of an exception prior to ending its task.
//
public class ChangesPublisher
{
// Set to true to stop publishing. This causes the Begin method to complete.
public bool Stop { get; set; }
// The event posted when data from the server arrived
public class ChangeEvent : EventArgs
{
public string Value { get; set; }
public ChangeEvent(string value)
{
Value = value;
}
}
// Event triggered when the subscriber croaks by exception
public class ExceptionEvent : EventArgs
{
public Exception Value { get; set; }
public ExceptionEvent(Exception value)
{
Value = value;
}
}
// Subscription to changes from the _changes endpoint
public event EventHandler<ChangeEvent> OnChange = delegate { };
// Subscription to publisher exit on error
public event EventHandler<ExceptionEvent> OnException = delegate { };
public async Task Begin(string serverAddr, int port, string query, string auth, int timeout)
{
using (var client = new TcpClient())
{
string request = String.Join("\r\n", new List<string> {
String.Format("GET /{0} HTTP/1.1",query),
"Authorization: " + auth,
"Accept: application/json",
"Host: " + serverAddr,
"Connection: keep-alive",
"\r\n"
});
try
{
await client.ConnectAsync(serverAddr, port);
using (NetworkStream stream = client.GetStream())
{
StreamWriter writer = new StreamWriter(stream);
await writer.WriteAsync(request);
await writer.FlushAsync();
// read lines from the server, ad nauseum.
StreamReader reader = new StreamReader(stream);
while (!Stop)
{
string data = await reader.ReadLineAsync();
// emit a change event
OnChange(this, new ChangeEvent(data));
}
}
}
catch (Exception e)
{
OnException(this, new ExceptionEvent(e));
}
}
}
}
}

RabbitMQ response is been lost in controller

Good evening everyone, I've got a web app written using .NET and a mobile app.
I'm sending some values to rabbitMQ server through my web app and this is working fine, i put it in a queue but when the mobile app accepts the request, i don't get the returned value.
Here is my controller
public async Task<ActionResult> GetCollect(int id)
{
int PartnerId = 0;
bool SentRequest = false;
try
{
SentRequest = await RuleRabbitMQ.SentRequestRule(id);
if(SentRequest )
{
PartnerId = await RuleRabbitMQ.RequestAccepted();
}
}
catch (Exception Ex)
{
}
}
This is my RabbitMQ class
public class InteractionRabbitMQ
{
public async Task<bool> SentRequestRule(int id)
{
bool ConnectionRabbitMQ = false;
await Task.Run(() =>
{
try
{
ConnectionFactory connectionFactory = new ConnectionFactory()
{
//credentials go here
};
IConnection connection = connectionFactory.CreateConnection();
IModel channel = connection.CreateModel();
channel.QueueDeclare("SolicitacaoSameDay", true, false, false, null);
string rpcResponseQueue = channel.QueueDeclare().QueueName;
string correlationId = Guid.NewGuid().ToString();
IBasicProperties basicProperties = channel.CreateBasicProperties();
basicProperties.ReplyTo = rpcResponseQueue;
basicProperties.CorrelationId = correlationId;
byte[] messageBytes = Encoding.UTF8.GetBytes(string.Concat(" ", id.ToString()));
channel.BasicPublish("", "SolicitacaoSameDay", basicProperties, messageBytes);
channel.Close();
connection.Close();
if (connection != null)
{
ConnectionRabbitMQ = true;
}
else
{
ConnectionRabbitMQ = false;
}
}
catch (Exception Ex)
{
throw new ArgumentException($"Thre was a problem with RabbitMQ server. " +
$"Pleaser, contact the support with Error: {Ex.ToString()}");
}
});
return ConnectionRabbitMQ;
}
public async Task<int> RequestAccepted()
{
bool SearchingPartner= true;
int PartnerId = 0;
await Task.Run(() =>
{
try
{
var connectionFactory = new ConnectionFactory()
{
// credentials
};
IConnection connection = connectionFactory.CreateConnection();
IModel channel = connection.CreateModel();
channel.BasicQos(0, 1, false);
var eventingBasicConsumer = new EventingBasicConsumer(channel);
eventingBasicConsumer.Received += (sender, basicDeliveryEventArgs) =>
{
string Response = Encoding.UTF8.GetString(basicDeliveryEventArgs.Body, 0, basicDeliveryEventArgs.Body.Length);
channel.BasicAck(basicDeliveryEventArgs.DeliveryTag, false);
if(!string.IsNullOrWhiteSpace(Response))
{
int Id = Convert.ToInt32(Response);
PartnerId = Id > 0 ? Id : 0;
SearchingPartner = false;
}
};
channel.BasicConsume("SolicitacaoAceitaSameDay", false, eventingBasicConsumer);
}
catch (Exception Ex)
{
// error message
}
});
return PartnerId ;
}
I am not sure this works, can't build an infrastructure to test this quickly, but - your issue is that the RequestAccepted returns a Task which completes before the Received event is caught by the Rabbit client library.
Syncing the two could possibly resolve the issue, note however that this could potentially make your code waiting very long for (or even - never get) the response.
public Task<int> RequestAccepted()
{
bool SearchingPartner= true;
int PartnerId = 0;
var connectionFactory = new ConnectionFactory()
{
// credentials
};
IConnection connection = connectionFactory.CreateConnection();
IModel channel = connection.CreateModel();
channel.BasicQos(0, 1, false);
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
var eventingBasicConsumer = new EventingBasicConsumer(channel);
eventingBasicConsumer.Received += (sender, basicDeliveryEventArgs) =>
{
string Response = Encoding.UTF8.GetString(basicDeliveryEventArgs.Body, 0, basicDeliveryEventArgs.Body.Length);
channel.BasicAck(basicDeliveryEventArgs.DeliveryTag, false);
if(!string.IsNullOrWhiteSpace(Response))
{
int Id = Convert.ToInt32(Response);
PartnerId = Id > 0 ? Id : 0;
SearchingPartner = false;
tcs.SetResult( PartnerId );
}
};
channel.BasicConsume("SolicitacaoAceitaSameDay", false, eventingBasicConsumer);
return tcs.Task;
}
There are couple of issues with this approach.
First, no error handling.
Then, what if the event is sent by the RMQ before the consumer subscribes to it? The consumer will block as it will never receive anything back.
And last, I don't think RMQ consumers are ever intended to be created in every request to your controller and then never disposed. While this could work on your dev box where you create a couple of requests manually, it won't probably ever scale to fix a scenario where dozens/hundreds of concurrent users hit your website and multiple RMQ consumers compete one against the other.
I don't think there is an easy way around it other than completely separate the consumer out of your web app, put it in a System Service or a Hangfire job and let it get responses to all possible requests and from the cache - serve responses to web requests.
This is a pure speculation, though, based on my understanding of what you try to do. I could be wrong here, of course.
byte[] messageBytes = Encoding.UTF8.GetBytes(string.Concat(" ", idColeta.ToString()));
I reckon 'idColeta' is blank.

SAS Provider for OLE DB (SAS.IOMProvider) doesn't work with ObjectPool

I'm using the SAS Integration Technologies COM components to connect to SAS Server from a C# .NET project. I want to submit statements to a SAS Workspace then load the output dataset from SAS using the OLE DB provider (SAS.IOMProvider). I am able to do this successfully using code like this:
static int Main(string[] args)
{
var keeper = new ObjectKeeper();
var factory = new ObjectFactoryMulti2();
var server = new ServerDef()
{
MachineDNSName = "sas.server.com",
Protocol = Protocols.ProtocolBridge,
Port = 8591,
BridgeSecurityPackage = "Negotiate",
};
var workspace = (IWorkspace)factory.CreateObjectByServer("Workspace1", true, server, null, null);
keeper.AddObject(1, workspace.UniqueIdentifier, workspace);
try
{
using (var conn = new OleDbConnection("Provider=SAS.IOMProvider.1; Data Source=iom-id://" + workspace.UniqueIdentifier))
{
// success
conn.Open();
}
}
catch (Exception ex)
{
System.Console.Error.WriteLine(ex.ToString());
return 1;
}
finally
{
keeper.RemoveObject(workspace);
workspace.Close();
}
return 0;
}
However, when I try using the ObjectPool feature of ObjectFactoryMulti2, the OLE DB connection doesn't work. It always throws "The object could not be found; make sure it was previously added to the object keeper." Here is the code that does not work:
static int Main(string[] args)
{
var keeper = new ObjectKeeper();
var factory = new ObjectFactoryMulti2();
var server = new ServerDef()
{
MachineDNSName = "sas.server.com`",
Protocol = Protocols.ProtocolBridge,
Port = 8591,
BridgeSecurityPackage = "Negotiate",
MaxPerObjectPool = Environment.ProcessorCount,
RunForever = true,
RecycleActivationLimit = 100,
};
var login = new LoginDef();
var pool = factory.ObjectPools.CreatePoolByServer("Pool1", server, login);
var lease = pool.GetPooledObject(null, null, 5000);
var workspace = (IWorkspace)lease.SASObject;
keeper.AddObject(1, workspace.UniqueIdentifier, workspace);
try
{
using (var conn = new OleDbConnection("Provider=SAS.IOMProvider.1; Data Source=iom-id://" + workspace.UniqueIdentifier))
{
// throws System.Data.OleDb.OleDbException: 'The object 1EFCE532-99BA-4A27-AF37-574EAE1CD04C could not be found; make sure it was previously added to the object keeper.'
conn.Open();
}
}
catch (Exception ex)
{
System.Console.Error.WriteLine(ex.ToString());
return 1;
}
finally
{
keeper.RemoveObject(workspace);
lease.ReturnToPool();
pool.Shutdown();
}
return 0;
}
Is there a way to use SAS connection pooling with the SAS OLE DB provider?
Got a good answer to this question from SAS Support. When using a connection pool, you have to cast the workspace to IServerStatus and connect using its ServerStatusUniqueID property instead of IWorkspace.UniqueIdentifier.
var pool = factory.ObjectPools.CreatePoolByServer("Pool1", server, login);
var lease = pool.GetPooledObject(null, null, 5000);
var workspace = (IWorkspace)lease.SASObject;
var status = (IServerStatus)lease.SASObject;
keeper.AddObject(1, workspace.UniqueIdentifier, workspace);
using (var conn = new OleDbConnection("Provider=SAS.IOMProvider.1; Data Source=iom-id://" + status.ServerStatusUniqueID))
{
// success
conn.Open();
}
keeper.RemoveObject(workspace);
lease.ReturnToPool();

.net remoting problem with chat server

I have a problem with my chat server implementation and I couldn't figure out why it doesn't work as intended.
The client could send messages to the server, but the server only sends the messages to itself instead of the client.
E.g. the client connects to the server, then types "hello" into the chat. The server successfully gets the message but then posts the message to its own console instead of sending it to the connected clients.
Well... maybe I have missed something as I'm very new to .Net remoting. Maybe someone could help me figure out what the problem is. Any help is appreciated!
The code:
I have a small interface for the chat implementation on the server
public class ChatService : MarshalByRefObject, IService
{
private Dictionary<string, IClient> m_ConnectedClients = new Dictionary<string, IClient>();
private static ChatService _Chat;
private ChatService()
{
Console.WriteLine("chat service created");
_Chat = this;
}
public bool Login(IClient user)
{
Console.WriteLine("logging in: " + user.GetIp());
if (!m_ConnectedClients.ContainsKey(user.GetIp()))
{
m_ConnectedClients.Add(user.GetIp(), user);
PostMessage(user.GetIp(), user.GetUserName() + " has entered chat");
return true;
}
return false;
}
public bool Logoff(string ip)
{
Console.WriteLine("logging off: " + ip);
IClient user;
if (m_ConnectedClients.TryGetValue(ip, out user))
{
PostMessage(ip, user + " has left chat");
m_ConnectedClients.Remove(ip);
return true;
}
return false;
}
public bool PostMessage(string ip, string text)
{
Console.WriteLine("posting message: " + text + " to: " + m_ConnectedClients.Values.Count);
foreach (var chatter in m_ConnectedClients.Values)
{
Console.WriteLine(chatter.GetUserName() + " : " + chatter.GetIp());
chatter.SendText(text);
}
return true;
}
}
My Server implements the chatservice as singleton:
RemotingConfiguration.RegisterWellKnownServiceType(typeof(ChatService), "chatservice", WellKnownObjectMode.Singleton);
My client is also simply straight forward:
[Serializable]
public class Chat_Client : IClient
{
private string m_IpAdresse;
private string m_UserName = "Jonny";
private string m_Input;
public Chat_Client(string ip, string username)
{
m_IpAdresse = ip;
m_UserName = username;
}
public bool HandleInput(string input)
{
if (input.Equals("exit"))
{
Client.m_ChatService.Logoff(m_IpAdresse);
return false;
}
m_Input = input;
Thread sendThread = new Thread(new ThreadStart(SendPostMessage));
sendThread.Start();
//Console.WriteLine("post message");
return true;
}
private void SendPostMessage()
{
Client.m_ChatService.PostMessage(m_IpAdresse, m_Input);
Thread thisThread = Thread.CurrentThread;
thisThread.Interrupt();
thisThread.Abort();
}
public void SendText(string text)
{
Console.WriteLine("send text got: " + text);
Console.WriteLine(text);
}
The main client connects to the server via:
public void Connect()
{
try
{
TcpChannel channel = new TcpChannel(0);
ChannelServices.RegisterChannel(channel, false);
m_ChatService = (IService)Activator.GetObject(typeof(IService), "tcp://" + hostname + ":9898/Host/chatservice");
System.Net.IPHostEntry hostInfo = Dns.GetHostEntry(Dns.GetHostName());
m_IpAdresse = hostInfo.AddressList[0].ToString();
Chat_Client client = new Chat_Client(m_IpAdresse, m_UserName);
Console.WriteLine("Response from Server: " + m_ChatService.Login(client));
string input = "";
while (m_Running)
{
input = Console.ReadLine();
m_Running = client.HandleInput(input);
}
}
}
#John: No, I wasn't aware of that. Thanks for the info, I'll look into it.
#Felipe: hostname is the dns name of the server I want to connect to.
I found a workaround to make this work. I added an additional TcpListener to the client to which the server connects when the client logs in. Over this second channel I transmit the chat messages back.
However I couldn't understand why the old solution does not work :<
Thanks for the hints guys.
The best thing to do with .NET remoting is to abandon it for WCF. If you read all the best practices for scalability, the way you end up using it is extremely compatible with the Web Services model, and web services is far easier to work with.
Remoting is technically fascinating and forms the basis of reflection, but falls apart once slow, unreliable connections are involved - and all connections are slow and unreliable compared to in-process messaging.

Categories

Resources