I have two apps, app1.cs and app2.cs (codes below). In addition I also have a dll I extracted from refer.cs(code below). When I compile app1.cs(which sends a measurement object) I get the following exception:
Unhandled Exception: RabbitMQ.Client.Exceptions.OperationInterruptioedException
I can't see how the connection is interrupted. Do you see where the problem is caused at?
Regards,
Demi
//refer.cs from which refer.dll is created
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace refer
{
//start alternate serialization
public static class AltSerialization
{
public static byte[] AltSerialize(Measurement m)
{
using (var ms = new MemoryStream())
{
var bf = new BinaryFormatter();
bf.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
bf.Serialize(ms, m);
return ms.GetBuffer();
}
}
public static Measurement AltDeSerialize(byte[] seriM)
{
using (var stream = new MemoryStream( seriM ))
{
BinaryFormatter bf = new BinaryFormatter();
bf.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
return (Measurement)bf.Deserialize(stream);
}
}
}
//end alternte serialization
[Serializable] //This attribute sets class to be serialized
public class Measurement : ISerializable
{
[NonSerialized] public int id;
public int time; //timestamp
public double value;
public Measurement()
{
id = 1;
time = 12;
value = 0.01;
}
public Measurement(int _id, int _time, double _value)
{
id = _id;
time = _time;
value = _value;
}
//Deserialization constructor
public Measurement(SerializationInfo info, StreamingContext ctxt)
{
//Assign the values from info to the approporiate properties
Console.WriteLine("DeSerialization construtor called.");
time = (int)info.GetValue("MeasurementTime", typeof(int));
value = (double)info.GetValue("MeasurementValue", typeof(double));
}
//Serialization function
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
// Custom name-value pair
// Values must be read with the same name they're written
info.AddValue("MeasurementTime", time);
info.AddValue("MeasurementValue", value);
}
}
}
//MB1.cs
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using UtilityMeasurement;
public interface IMessageBus
{
string MsgSys // Property 1
{
get;
set;
}
void write (Measurement m1);
Measurement read();
void publish(string queue);
void subscribe(string queue);
}
public class Rabbit : IMessageBus
{
// Implementation of methods for Rabbit class go here
private List<string> publishQ = new List<string>();
private List<string> subscribeQ = new List<string>();
public void write ( Measurement m1 )
{
byte[] body = Measurement.AltSerialize( m1 );
IConnection connection = factory.CreateConnection();
IModel channel = connection.CreateModel();
foreach (string queue in publishQ)
{
channel.BasicPublish("", queue, null, body);
Console.WriteLine("\n [x] Sent to queue {0}.", queue);
}
}
public void publish(string queueName)
{
channel.QueueDeclare(queueName, true, false, false, null); //durable=true
publishQ.Add(queueName); //and, add it the list of queue names to publish to
}
public Measurement read()
{
QueueingBasicConsumer consumer = new QueueingBasicConsumer(channel);
foreach (string queue in subscribeQ)
{
channel.BasicConsume(queue, true, consumer);
}
System.Console.WriteLine(" [*] Waiting for messages." +
"To exit press CTRL+C");
BasicDeliverEventArgs ea =
(BasicDeliverEventArgs)consumer.Queue.Dequeue();
return Measurement.AltDeSerialize(ea.Body);
}
public void subscribe(string queueName)
{
channel.QueueDeclare(queueName, true, false, false, null);
subscribeQ.Add(queueName);
}
public static string MsgSysName;
public string MsgSys
{
get
{
return MsgSysName;
}
set
{
MsgSysName = value;
}
}
public Rabbit(string _msgSys) //Constructor
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = "localhost";
System.Console.WriteLine("\nMsgSys: RabbitMQ");
MsgSys = _msgSys;
}
}
public class Zmq : IMessageBus
{
public void write ( Measurement m1 )
{
//
}
public Measurement read()
{
//
return null;
}
public void publish(string queue)
{
//
}
public void subscribe(string queue)
{
//
}
public static string MsgSysName;
public string MsgSys
{
get
{
return MsgSysName;
}
set
{
MsgSysName = value;
}
}
// Implementation of methods for Zmq class go here
public Zmq(string _msgSys) //Constructor
{
System.Console.WriteLine("ZMQ");
MsgSys = _msgSys;
}
}
public class MessageBusFactory
{
public static IMessageBus GetMessageBus(string MsgSysName)
{
switch ( MsgSysName )
{
case "Zmq":
return new Zmq(MsgSysName);
case "Rabbit":
return new Rabbit(MsgSysName);
default:
throw new ArgumentException("Messaging type " +
MsgSysName + " not supported." );
}
}
}
public class MainClass
{
public static void Main()
{
//Asks for the message system
System.Console.WriteLine("\nEnter name of messageing system: ");
System.Console.WriteLine("Usage: [Rabbit] [Zmq]");
string MsgSysName = (System.Console.ReadLine()).ToString();
//Create a new Measurement message
Measurement m1 = new Measurement(2, 2345, 23.456);
//Declare an IMessageBus instance:
//Here, an object of the corresponding Message System
// (ex. Rabbit, Zmq, etc) is instantiated
IMessageBus obj1 = MessageBusFactory.GetMessageBus(MsgSysName);
System.Console.WriteLine("\nA {0} object is now created.", MsgSysName);
System.Console.WriteLine("With Test message:\n ID: {0}", m1.id);
System.Console.WriteLine(" Time: {0}", m1.time);
System.Console.WriteLine(" Value: {0}", m1.value);
// Ask queue name and store it
System.Console.WriteLine("Enter a queue name to publish the message to: ");
string QueueName = (System.Console.ReadLine()).ToString();
obj1.publish( QueueName );
System.Console.WriteLine("Enter another queue name: ");
QueueName = (System.Console.ReadLine()).ToString();
obj1.publish( QueueName );
// Write message to the queue
obj1.write( m1 );
}
}
//MB2.cs
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using UtilityMeasurement;
public interface IMessageBus
{
string MsgSys // Property 1
{
get;
set;
}
void write (Measurement m1);
Measurement read();
void publish(string queue);
void subscribe(string queue);
}
public class Rabbit : IMessageBus
{
// Implementation of methods for Rabbit class go here
private List<string> publishQ = new List<string>();
private List<string> subscribeQ = new List<string>();
public void write ( Measurement m1 )
{
byte[] body = Measurement.AltSerialize( m1 );
IConnection connection = factory.CreateConnection();
IModel channel = connection.CreateModel();
foreach (string queue in publishQ)
{
channel.BasicPublish("", queue, null, body);
Console.WriteLine("\n [x] Sent to queue {0}.", queue);
}
}
public void publish(string queueName)
{
channel.QueueDeclare(queueName, true, false, false, null); //durable=true
publishQ.Add(queueName); //and, add it the list of queue names to publish to
}
public Measurement read()
{
QueueingBasicConsumer consumer = new QueueingBasicConsumer(channel);
foreach (string queue in subscribeQ)
{
channel.BasicConsume(queue, true, consumer);
}
System.Console.WriteLine(" [*] Waiting for messages." +
"To exit press CTRL+C");
BasicDeliverEventArgs ea =
(BasicDeliverEventArgs)consumer.Queue.Dequeue();
return Measurement.AltDeSerialize(ea.Body);
}
public void subscribe(string queueName)
{
channel.QueueDeclare(queueName, true, false, false, null);
subscribeQ.Add(queueName);
}
public static string MsgSysName;
public string MsgSys
{
get
{
return MsgSysName;
}
set
{
MsgSysName = value;
}
}
public Rabbit(string _msgSys) //Constructor
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = "localhost";
System.Console.WriteLine("\nMsgSys: RabbitMQ");
MsgSys = _msgSys;
}
}
public class Zmq : IMessageBus
{
public void write ( Measurement m1 )
{
//
}
public Measurement read()
{
//
return null;
}
public void publish(string queue)
{
//
}
public void subscribe(string queue)
{
//
}
public static string MsgSysName;
public string MsgSys
{
get
{
return MsgSysName;
}
set
{
MsgSysName = value;
}
}
// Implementation of methods for Zmq class go here
public Zmq(string _msgSys) //Constructor
{
System.Console.WriteLine("ZMQ");
MsgSys = _msgSys;
}
}
public class MessageBusFactory
{
public static IMessageBus GetMessageBus(string MsgSysName)
{
switch ( MsgSysName )
{
case "Zmq":
return new Zmq(MsgSysName);
case "Rabbit":
return new Rabbit(MsgSysName);
default:
throw new ArgumentException("Messaging type " +
MsgSysName + " not supported." );
}
}
}
public class MainClass
{
public static void Main()
{
//Asks for the message system
System.Console.WriteLine("\nEnter name of messageing system: ");
System.Console.WriteLine("Usage: [Rabbit] [Zmq]");
string MsgSysName = (System.Console.ReadLine()).ToString();
//Declare an IMessageBus instance:
//Here, an object of the corresponding Message System
// (ex. Rabbit, Zmq, etc) is instantiated
IMessageBus obj1 = MessageBusFactory.GetMessageBus(MsgSysName);
System.Console.WriteLine("\nA {0} object is now created.", MsgSysName);
System.Console.WriteLine("Enter a queue to subscribe to: ");
string QueueName = (System.Console.ReadLine()).ToString();
obj1.subscribe( QueueName );
//Create a new Measurement object m2
Measurement m2 = new Measurement();
//Read message into m2
m2 = obj1.read();
m2.id = 11;
System.Console.WriteLine("\nMessage received from queue {0}:\n ID: {1}",QueueName, m2.id);
System.Console.WriteLine(" Time: {0}", m2.time);
System.Console.WriteLine(" Value: {0}", m2.value);
}
}
I just created a vanilla C# VS2010 Console application project with the Refer.cs and App1.cs in the same project.
I made the following changes:
Added RabbitMQ.Client.dll
Removed the AssemblyVersion attributes
Added string[] args to the Main method in App1.cs
Also, I changed:
factory.HostName = "localhost";
To this:
factory.HostName = "192.168.56.101";
Which is the ip address to my VirtualBox Ubuntu VM running rabbitmq-server. There was no exception thrown, and the message successfully was received on the server.
All signs point to server configuration with what is given. My guess is either your rabbitmq-server is not running at all, it's not running on localhost, or there is some kind of connectivity issue with port 5672.
Related
so iv been having trouble trying to reconnect clients to the same lobby after they leave.
Creating a public lobby then joining it using QuickJoinLobbyAsync seems to work just fine, but if a client leaves a lobby they unable to reconnect to that same lobby. when trying to connect again I get this error : "[Lobby]: NoOpenLobbies, (16006). Message: failed to find any open lobbies matching the search criteria"
The following script is attached to a GameObject that is active in the hierarchy.
I have buttons that when pressed activate certain functions such as CreateAMatch()
There are 2 scenes in my project, Multiplayer and MainMenu, This script is used in MultiPlayer
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Netcode;
using Unity.Services.Authentication;
using Unity.Services.Core;
using Unity.Services.Lobbies;
using Unity.Services.Lobbies.Models;
using Unity.Services.Relay;
using Unity.Services.Relay.Models;
using UnityEngine;
using UnityEngine.Events;
using Unity.Netcode.Transports.UTP;
public class GameManagerV2 : MonoBehaviour
{
public static GameManagerV2 _instance;
public static GameManagerV2 Instance => _instance;
public RelayHostData _hostData;
public RelayJoinData _joinData;
public string joinCode;
public string lobbyId;
public string lobbyjoinCode;
public UnityAction<string> UpdateState;
// Notify Match found
public UnityAction MatchFound;
public List<string> lobbyPlayers = new List<string>();
async void Awake()
{
}
async void Start()
{
// UnityServices.InitializeAsync() will initialize all services that are subscribed to Core.
await UnityServices.InitializeAsync();
Debug.Log(UnityServices.State);
SetupEvents();
await SignInAnonymouslyAsync();
NetworkManager.Singleton.OnClientConnectedCallback += ClientConnected;
NetworkManager.Singleton.OnClientDisconnectCallback += ClientDisconnected;
}
// Setup authentication event handlers if desired
void SetupEvents()
{
AuthenticationService.Instance.SignedIn += () => {
// Shows how to get a playerID
Debug.Log($"PlayerID: {AuthenticationService.Instance.PlayerId}");
// Shows how to get an access token
Debug.Log($"Access Token: {AuthenticationService.Instance.AccessToken}");
};
AuthenticationService.Instance.SignInFailed += (err) => {
Debug.LogError(err);
};
AuthenticationService.Instance.SignedOut += () => {
Debug.Log("Player signed out.");
};
AuthenticationService.Instance.Expired += () =>
{
Debug.Log("Player session could not be refreshed and expired.");
};
}
async Task SignInAnonymouslyAsync()
{
try
{
await AuthenticationService.Instance.SignInAnonymouslyAsync();
Debug.Log("Sign in anonymously succeeded!");
// Shows how to get the playerID
Debug.Log($"PlayerID: {AuthenticationService.Instance.PlayerId}");
}
catch (AuthenticationException ex)
{
// Compare error code to AuthenticationErrorCodes
// Notify the player with the proper error message
Debug.LogException(ex);
}
catch (RequestFailedException ex)
{
// Compare error code to CommonErrorCodes
// Notify the player with the proper error message
Debug.LogException(ex);
}
}
private void ClientConnected(ulong id)
{
// Player with id connected to our session
Debug.Log("Connected player with id: " + id);
UpdateState?.Invoke("Player found!");
MatchFound?.Invoke();
}
private async void ClientDisconnected(ulong id)
{
lobbyId.ToString();
string playerId = AuthenticationService.Instance.PlayerId;
Debug.Log("The Player : " + playerId + " Has left from lobby : " + lobbyId);
await LobbyService.Instance.RemovePlayerAsync(lobbyId, playerId);
}
public struct RelayHostData
{
public string JoinCode;
public string IPv4Address;
public ushort Port;
public Guid AllocationID;
public byte[] AllocationIDBytes;
public byte[] ConnectionData;
public byte[] Key;
}
/// <summary>
/// RelayHostData represents the necessary informations
/// for a Host to host a game on a Relay
/// </summary>
public struct RelayJoinData
{
public string JoinCode;
public string IPv4Address;
public ushort Port;
public Guid AllocationID;
public byte[] AllocationIDBytes;
public byte[] ConnectionData;
public byte[] HostConnectionData;
public byte[] Key;
}
public async void CreateAMatch()
{
Debug.Log("Creating a new lobby...");
UpdateState?.Invoke("Creating a new match...");
int maxConnections = 20;
try
{
Allocation allocation = await Relay.Instance.CreateAllocationAsync(maxConnections);
_hostData = new RelayHostData
{
Key = allocation.Key,
Port = (ushort)allocation.RelayServer.Port,
AllocationID = allocation.AllocationId,
AllocationIDBytes = allocation.AllocationIdBytes,
ConnectionData = allocation.ConnectionData,
IPv4Address = allocation.RelayServer.IpV4
};
Debug.Log("Key is " + allocation.Key);
_hostData.JoinCode = await Relay.Instance.GetJoinCodeAsync(allocation.AllocationId);
CreateLobbyOptions options = new CreateLobbyOptions();
string lobbyName = "new_lobby";
int maxPlayers = 20;
options.IsPrivate = false;
options.Data = new Dictionary<string, DataObject>()
{
{
"joinCode", new DataObject(
visibility: DataObject.VisibilityOptions.Public,
value: _hostData.JoinCode)
},
};
Lobby lobby = await LobbyService.Instance.CreateLobbyAsync(lobbyName, maxPlayers, options);
lobbyId = lobby.Id;
Debug.Log("This Is the RAW Lobby Id : " + lobby.Id);
Debug.Log("This Is the RAW Join Code: " + _hostData.JoinCode);
StartCoroutine(HeartbeatLobbyCoroutine(lobby.Id, 15));
NetworkManager.Singleton.GetComponent<UnityTransport>().SetRelayServerData(
_hostData.IPv4Address,
_hostData.Port,
_hostData.AllocationIDBytes,
_hostData.Key,
_hostData.ConnectionData);
NetworkManager.Singleton.StartHost();
UpdateState?.Invoke("Waiting for players...");
Debug.Log("Match Created! ");
}
catch (LobbyServiceException e)
{
Console.WriteLine(e);
throw;
}
}
IEnumerator HeartbeatLobbyCoroutine(string lobbyId, float waitTimeSeconds)
{
var delay = new WaitForSecondsRealtime(waitTimeSeconds);
while (true)
{
Lobbies.Instance.SendHeartbeatPingAsync(lobbyId);
Debug.Log("Lobby Heartbit");
yield return delay;
}
}
public async void JoinACreatedMatch()
{
try
{
QuickJoinLobbyOptions options = new QuickJoinLobbyOptions();
options.Filter = new List<QueryFilter>()
{
new QueryFilter(
field: QueryFilter.FieldOptions.MaxPlayers,
op: QueryFilter.OpOptions.GE,
value: "20")
};
var lobby = await LobbyService.Instance.QuickJoinLobbyAsync(options);
string joinCode = lobby.Data["joinCode"].Value;
lobbyId = lobby.Id;
lobbyjoinCode = lobby.Data["joinCode"].Value;
JoinAllocation allocation = await Relay.Instance.JoinAllocationAsync(joinCode);
_joinData = new RelayJoinData
{
Key = allocation.Key,
Port = (ushort)allocation.RelayServer.Port,
AllocationID = allocation.AllocationId,
AllocationIDBytes = allocation.AllocationIdBytes,
ConnectionData = allocation.ConnectionData,
HostConnectionData = allocation.HostConnectionData,
IPv4Address = allocation.RelayServer.IpV4
};
NetworkManager.Singleton.GetComponent<UnityTransport>().SetRelayServerData(
_joinData.IPv4Address,
_joinData.Port,
_joinData.AllocationIDBytes,
_joinData.Key,
_joinData.ConnectionData,
_joinData.HostConnectionData);
// Finally start the client
NetworkManager.Singleton.StartClient();
}
catch (LobbyServiceException e)
{
Debug.Log(e);
}
}
}
As for the disconnect function:
This script is attached to the player prefab.
inside of void update.
if (IsOwner)
{
if (Input.GetKeyDown(KeyCode.O))
{
NetworkManager.Singleton.Shutdown();
SceneManager.LoadScene("MultiPlayer");
}
}
PS: I'm very new to programing and unity in general
At first i noticed i had no OnClientDisconnectCallback method, and so i promptly added that. later i added a disconnect function to the client so that upon a key press the client would end its connection "NetworkManager.Singleton.Shutdown();" i thought that this would both indicate to the host that a client had left, and the client was no longer connected.
So for me... it turns out I was using a "ConnectionNotificationManager.cs" that I then Destroyed on the client side after connection.
This resulted in the client callback registered to the NetworkManager becoming null. Then I did not get my call back.
Perhaps you are unregistering your callback or destroying the object.
I'm creating a program which will execute a command after user input.
Some commands I want to implement are: creating, reading a file, getting current working directory etc.
I created a dictionary which will store user input and corresponding command:
public static Dictionary<string, Action<string[]>> Commands { get; set; } = new Dictionary<string, Action<string[]>>()
{
{"pwd", PrintWorkingDirectory },
{"create", CreateFile },
{"print", ReadFile },
};
Unfortunately I have issues with triggering the method:
public void Run()
{
Console.WriteLine("Welcome, type in command.");
string input = null;
do
{
Console.Write("> ");
input = Console.ReadLine();
Execute(input);
} while (input != "exit");
}
public int Execute(string input)
{
if(Commands.Keys.Contains(input))
{
var action = Commands.Values.FirstOrDefault(); //doesn't work, gives '{command} not found'
}
Console.WriteLine($"{input} not found");
return 1;
}
Also I noticed that this solution would not work with method which is not void, but returns something, as for example CreateFile.
public static string CreateFile(string path)
{
Console.WriteLine("Create a file");
string userInput = Console.ReadLine();
try
{
string[] file = userInput.Split(new char[] { ' ' }).Skip(1).ToArray();
string newPath = Path.GetFullPath(Path.Combine(file));
using (FileStream stream = new FileStream(newPath, FileMode.Create, FileAccess.ReadWrite))
{
stream.Close();
}
using (StreamWriter sw = new StreamWriter(newPath))
{
Console.WriteLine("Please type the content.Press Enter to save.");
sw.WriteLine(Console.ReadLine());
sw.Close();
Console.WriteLine("File {0} has been created", newPath);
}
}
catch (Exception)
{
throw;
}
return path;
}
public static void ReadFile(string[] args)
{
Console.WriteLine("Reading file");
string userInput = Console.ReadLine();
string[] file = userInput.Split(new char[] { ' ' }).Skip(1).ToArray();
string newPath = Path.GetFullPath(Path.Combine(file));
string[] lines = File.ReadAllLines(newPath);
foreach (string line in lines)
Console.WriteLine(line);
}
public static void PrintWorkingDirectory(string[] args)
{
var currentDirectory = Directory.GetCurrentDirectory();
Console.WriteLine(currentDirectory);
}
Could somebody advise me how to deal with these issues?
Is it that this dictionary I created does not make much sense at all?
First problem: You're always fetching the first element of the dictionary and are not using the index operator to retrieve the correct value. Therefore change:
if(Commands.Keys.Contains(input))
{
var action = Commands.Values.FirstOrDefault(); //doesn't work, gives '{command} not found'
}
to:
public int Execute(string input)
{
if (Commands.Keys.Contains(input))
{
var action = Commands[input]; //doesn't work, gives '{command} not found'
action?.Invoke(new string[] { });
}
else
{
Console.WriteLine($"{input} not found");
}
return 1;
}
Regarding to your second question about dictionary usage. I think it is ok to use a dictionary to map different commands based on a given key. The alternative would be switch or if constructs, which can be prevented in Object Oriented Programming.
Regarding to your question about string CreateFile(string path). Since C# is strongly typed language your dictionary can only contain objects of type Action<string[]>, so you can't use methods with another signature than that. One solution is to add another dictionary in the form of Dictionary<string,Func<string[], string>. As a result you'll get more and more dictionaries depending on your method signatures. From here on you should think to build to encapsulate your commands in an e.g. CommandInterpreter class, that could offer an API like that:
void Request(string cmdName, string[] cmdParameters);
string GetLastResult();
int GetLastCode();
Update:
Below code shows a possible object oriented solution (I've left out interfaces to make the code more compact):
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
public class Command<T>
{
public string Name { get; }
public T TheCommand { get; }
public Command(string name, T theCommand)
{
Name = name;
TheCommand = theCommand;
}
}
public interface ICommandResult
{
void Ok(Action<ICommandResult> yes, Action<ICommandResult> no);
int Code { get; }
string Description { get; }
}
public abstract class CommandResult : ICommandResult
{
public int Code { get; }
public string Description { get; }
protected CommandResult(int code, string description)
{
Code = code;
Description = description;
}
public abstract void Ok(Action<ICommandResult> yes, Action<ICommandResult> no);
}
public class NullCommandResult : CommandResult
{
public NullCommandResult() : base(-1, "null")
{
}
public override void Ok(Action<ICommandResult> yes, Action<ICommandResult> no) => no?.Invoke(this);
}
public class SuccessCommandResult : CommandResult
{
public SuccessCommandResult(string description) : base(0, description)
{
}
public override void Ok(Action<ICommandResult> yes, Action<ICommandResult> no) => yes?.Invoke(this);
}
public class CommandInterpreter
{
private Dictionary<string, Func<IEnumerable<string>, ICommandResult>> Commands = new Dictionary<string, Func<IEnumerable<string>, ICommandResult>>();
public void RegisterCommand(Command<Func<IEnumerable<string>, ICommandResult>> cmd)
=> Commands.Add(cmd.Name, cmd.TheCommand);
public ICommandResult RunCommand(string name, IEnumerable<string> parameters)
=> Commands.Where(kvp => kvp.Key.Equals(name))
.Select(kvp => kvp.Value)
.DefaultIfEmpty(strArr => new NullCommandResult())
.Single()
.Invoke(parameters);
}
class Program
{
private CommandInterpreter _cmdInterpreter;
private Program()
{
_cmdInterpreter = new CommandInterpreter();
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("pwd", PrintWorkingDirectory));
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("create", CreateFile));
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("print", ReadFile));
}
private static CommandResult ReadFile(IEnumerable<string> arg) => new SuccessCommandResult("File read");
private static CommandResult CreateFile(IEnumerable<string> arg) => new SuccessCommandResult("File xyz created");
private static CommandResult PrintWorkingDirectory(IEnumerable<string> arg) => new SuccessCommandResult("Printed something");
static void Main() => new Program().Run();
private void Run()
{
Console.WriteLine("Welcome, type in command.");
string input;
do
{
Console.Write("> ");
input = Console.ReadLine();
var cmdResult = _cmdInterpreter.RunCommand(input, Enumerable.Empty<string>());
cmdResult.Ok(
r => Console.WriteLine($"Success: {cmdResult.Code}, {cmdResult.Description}"),
r => Console.WriteLine($"FAILED: {cmdResult.Code}, {cmdResult.Description}"));
} while (input != "exit");
}
}
}
Output:
Welcome, type in command.
> pwd
Success: 0, Printed something
> create
Success: 0, File xyz created
> abc
FAILED: -1, null
>
You can just copy the code and play around with it.
How do I separate my code into their own classes and still have it function the same? This is currently what my code looks like.
using System;
using System.Collections.Generic;
using System.Xml;
using XCENT.JobServer.JobPlugIn;
using System.IO;
using HPD.API.Utility.DataAccess;
namespace DataPurge
{
public class Purge : IJob, IJobControl {
public IJobControl JobControl { get { return ( this ); } }
public int MaxInstanceCount { get { return 1; } }
public string Name { get { return "DataPurge"; } }
public Purge() { }
public void Run( string XmlFragment ) {
XmlNode xmlNode = null;
try
{
xmlNode = Common.ConstructXmlNodeFromString(XmlFragment, "Params");
var list = DataList();
foreach (var item in list)
{
var factory = new PurgerFactory(item);
IPurger purge = factory.Purger;
purge.Purge();
purge = null;
factory = null;
}
}
catch (Exception ex)
{
throw;
}
}
public interface IPurger
{
void Purge();
}
public enum PurgeType
{
File,
Database,
}
public class FilePurger : IPurger
{
private Parameters parameter;
public FilePurger(Parameters parameter)
{
this.parameter = parameter;
}
public void Purge()
{
var files = new DirectoryInfo(parameter.FilePath).GetFiles();
foreach (var file in files)
{
if (DateTime.Now - file.CreationTime > TimeSpan.FromDays(7))
{
File.Delete(file.FullName);
}
}
}
}
public class DbPurger : IPurger
{
private Parameters parameter;
public DbPurger(Parameters parameter)
{
this.parameter = parameter;
}
public void Purge()
{
var access = new SqlDataAccess();
var sqlParams = new Dictionary<string, object>();
sqlParams.Add("#OlderThanDays", parameter.OlderThanDays);
access.ExecuteNonQuery(parameter.CString, parameter.SPName, sqlParams, 30, false);
}
}
private List<Parameters> DataList()
{
var sqlParams = new SqlDataAccess();
var list = sqlParams.GetDataTableAsList<Parameters>("Data Source = MYSERVER; Initial Catalog = MYDATABASE; User ID = UID; Password = PASSWORD;", "purge.spoDataTable", null);
return list;
}
public class PurgerFactory
{
public IPurger Purger { get; set; }
public PurgerFactory(Parameters parameter)
{
PurgeType type = (PurgeType)Enum.Parse(typeof(PurgeType), parameter.PurgeType);
switch (type)
{
case PurgeType.File:
Purger = new FilePurger(parameter);
break;
case PurgeType.Database:
Purger = new DbPurger(parameter);
break;
default:
throw new NotImplementedException();
}
}
}
/// <summary>
/// Used to submit a job via the job monitor
/// </summary>
public XmlNode JobXMLNode => Common.ConstructXmlNodeFromString("" +
"<JobParams>" +
" <Param Name=\"InfrastructureAPI\" DataType=\"String\">" +
" <Description>Infrastructure API URL.</Description>" +
" </Param>" +
" <Param Name=\"EnvironmentName\" DataType=\"String\">" +
" <Description>The current environment.</Description>" +
" </Param>" +
"</JobParams>",
"JobParams");
}
}
Currently all parts of the program are stuffed into this one single class. I want to separate them out into their own separate classes to make the code much cleaner but still have it function the same. I'm still a beginner coder and don't know the first place to start. Any help would be much appreciated!
You should create a file IPurger.cs for the interface IPurger, then a file FilePurger.cs for the class FilePurger, the file DbPurger.cs for the class DbPurger and lastly PurgerFactory.cs for the class PurgerFactory.
That should clean up your code quite well.
If that enum is used from multiple places, you may want to place it in its own class too, perhaps a generic Enums.cs.
I have a program called Scenario2_Client.xaml.cs that has the following function:
namespace SDKTemplate
{
public sealed partial class Scenario2_Client : Page
{
private MainPage rootPage = MainPage.Current;
// code
// this is the function I would like to call
private void RemoveValueChangedHandler()
{
ValueChangedSubscribeToggle.Content = "Subscribe to value changes";
if (subscribedForNotifications)
{
registeredCharacteristic.ValueChanged -= Characteristic_ValueChanged;
registeredCharacteristic = null;
subscribedForNotifications = false;
}
}
// ...
}
}
And then I have added a class in a different file (but in the same project) called EchoClient.cs which has the following code:
namespace SDKTemplate
{
class EchoClient
{
public void TcpClient()
{
try
{
TcpClient client = new TcpClient("139.169.63.130", 9999);
StreamReader reader = new StreamReader(client.GetStream());
StreamWriter writer = new StreamWriter(client.GetStream());
string s = string.Empty;
while (!s.Equals("Exit"))
{
Console.WriteLine("TCP Client connected....");
Console.WriteLine("Enter a string or number to send to the server: ");
s = Console.ReadLine();
writer.WriteLine(s);
writer.Flush();
string server_string = reader.ReadLine();
Console.WriteLine(server_string);
}
reader.Dispose();
writer.Dispose();
client.Dispose();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
internal class Console
{
internal static string ReadLine()
{
throw new NotImplementedException();
}
internal static void WriteLine(string v)
{
throw new NotImplementedException();
}
internal static void WriteLine(Exception e)
{
throw new NotImplementedException();
}
internal class TcpClient
{
private string v1;
private int v2;
public TcpClient(string v1, int v2)
{
this.v1 = v1;
this.v2 = v2;
}
internal void Dispose()
{
throw new NotImplementedException();
}
internal Stream GetStream()
{
throw new NotImplementedException();
}
}
}
Is there a way to call that function from this class?
I would have done something like this if it was public:
EchoClient client = new EchoClient()
client.somefunction();
client.somefunction();
..but since this method is private, how should I access it?
It is possible to invoke a private method using reflection as follows.
var iMethod
= client.GetType().GetMethod("somefunction",
BindingFlags.NonPublic | BindingFlags.Instance);
iMethod.Invoke(client, new object[]{});
I'm not sure why #Codor was down-voted, but here's the same answer fleshed out a little more. First I create a class with a private method:
public class PrivateFunction
{
private int _age;
public PrivateFunction(int age)
{
_age = age;
}
private int DoSomethingPrivate(string parameter)
{
Debug.WriteLine($"Parameter: {parameter}, Age: {_age}");
return _age;
}
}
I created a method that takes parameters and returns an integer to show all possibilities.
Then I call it:
var type = typeof(PrivateFunction);
var func = type.GetMethod("DoSomethingPrivate", BindingFlags.Instance | BindingFlags.NonPublic);
var obj = new PrivateFunction(12);
var ret = func.Invoke(obj, new[] {"some parameter"});
Debug.WriteLine($"Function returned {ret}");
and I get this in the output (proving something happened):
Parameter: some parameter, Age: 12
Function returned 12
If you are going to repeatedly call the same function (perhaps with different objects), save the MethodInfo object in func. It's immutable and re-useable.
I got a big issue trying to make this work.
I have a WCF scenario, with callbacks, trying to send a Message that is a struct made of a string and an object.
Seems like the object can't be serialized.
I get the following error trying to call a function sending that data:
Type '<>f__AnonymousType01[System.String]' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. See the Microsoft .NET Framework documentation for other supported types.
The code for my CLIENT IS:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.Serialization;
using System.IO;
namespace WCFClient
{
[DataContract]
public struct Message
{
private string messageType;
private object param;
public Message(string _messageType, object _param)
{
// TODO: Complete member initialization
this.messageType = _messageType;
this.param = _param;
}
[DataMember]
public string type { get { return messageType; } set { messageType = type; } }
[DataMember]
public object obj { get { return param; } set { param = obj; } }
}
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(ICallbacks))]
public interface IMessageHandler
{
[OperationContract]
void HandleMessage(string value);
}
public interface ICallbacks
{
[OperationContract(IsOneWay = true)]
void QueuePaths_Callback(string cPath, string EPath, string RPath, string IPath, string OPath);
[OperationContract(IsOneWay = true)]
void MyCallbackFunction(Message msg);
}
public class Callbacks : ICallbacks
{
public void QueuePaths_Callback(string cPath, string EPath, string RPath, string IPath, string OPath)
{
Console.WriteLine("Callback Received: ");
}
public void MyCallbackFunction(Message msg)
{
Console.WriteLine("Callback Received: ");
}
}
class Program
{
static void Main(string[] args)
{
Callbacks myCallbacks = new Callbacks();
DuplexChannelFactory<IMessageHandler> pipeFactory =
new DuplexChannelFactory<IMessageHandler>(
myCallbacks,
new NetNamedPipeBinding(),
new EndpointAddress(
"net.pipe://localhost/PipeReverse"));
IMessageHandler pipeProxy =
pipeFactory.CreateChannel();
while (true)
{
string str = Console.ReadLine();
pipeProxy.HandleMessage(str);//send the type for example
}
}
}
}
And my SERVER code:
using System;
using System.ServiceModel;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Text;
namespace WCFServer
{
[DataContract]
public struct Message
{
private string messageType;
private object param;
public Message(string _messageType, object _param)
{
// TODO: Complete member initialization
this.messageType = _messageType;
this.param = _param;
}
[DataMember]
public string type { get { return messageType; } set { messageType = type; } }
[DataMember]
public object obj { get { return param; } set { param = obj; } }
}
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(ICallbacks))]
public interface IMessageHandler
{
[OperationContract]
void HandleMessage(string value);
}
public interface ICallbacks
{
[OperationContract(IsOneWay = true)]
void QueuePaths_Callback(string cPath, string EPath, string RPath, string IPath, string OPath);
[OperationContract(IsOneWay = true)]
void MyCallbackFunction(Message msg);
}
public class StringReverser : IMessageHandler
{
public void HandleMessage(string value)//handle the type and do the request
{
ICallbacks callbacks = OperationContext.Current.GetCallbackChannel<ICallbacks>();
if (value.CompareTo("1") == 0)
{
callbacks.QueuePaths_Callback("path1", "path2", "path3", "path4", "path5");
}
else
{
Console.WriteLine("In the else");
//BinaryFormatter bformatter = new BinaryFormatter();
//MemoryStream stream = new MemoryStream();
//bformatter.Serialize(stream, new Message(value, new { PathCompleted = "path" }));
callbacks.MyCallbackFunction(new Message(value, new { PathCompleted = "path" }));
}
}
}
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(
typeof(StringReverser),
new Uri[]{
new Uri("net.pipe://localhost")
}))
{
host.AddServiceEndpoint(typeof(IMessageHandler),
new NetNamedPipeBinding(),
"PipeReverse");
host.Open();
Console.WriteLine("Service is available. " +
"Press <ENTER> to exit.");
Console.ReadLine();
host.Close();
}
}
}
}
Why do I have this issue with objects? They are annonymous I know but in the Types Supported by the Data Contract Serializer from MSDN!(http://msdn.microsoft.com/en-us/library/ms731923.aspx) they say .NET Framework primitive types. The following types built into the .NET Framework can all be serialized and are considered to be primitive types: Byte, SByte, Int16, Int32, Int64, UInt16, UInt32, UInt64, Single, Double, Boolean, Char, Decimal, Object, and String. so I don't know why I cant.
Thank you, I appreciate your help