I have a List in Class 1.
I have a helper class (Class 2) which is instantiated inside Class 1.
Class 2 should then do some work and update the List in Class 1.
I tried passing Class 1 into the Constructor of Class 2, but this only modifies the version within Class 2.
public class Server // Class 1
{
private string m_serverName;
private string m_loginUsername;
private string m_loginPassword;
private ServerConnectionTools m_connectionTools;
public List<Service> Services { get; private set; }
public Server(string serverName, string loginUsername, string loginPassword)
{
this.ServerName = serverName;
this.LoginUsername = loginUsername;
this.LoginPassword = loginPassword;
// Login to server and retrieve list of services
ConnectionTools = new ServerConnectionTools(this);
}
}
public class ServerConnectionTools // Class 2
{
private Server m_server;
private ManagementScope m_scope;
public ServerConnectionTools(Server server)
{
this.Server = server;
this.Scope = InitiateScope();
try
{
// Once this is finished updating, I need to update the Service List in Class 1.
this.UpdateServicesList();
}
catch (System.Runtime.InteropServices.COMException)
{
// Server is unavailable
Console.WriteLine("Unable to reach server {0}", server.ServerName);
}
}
public ManagementScope InitiateScope()
{
ManagementScope scope;
// If server is Remote server, log in with Credentials
// otherwise no need to connect.
if (System.Environment.MachineName.ToLower() != this.Server.ServerName.ToLower())
{
// Is a remote server, need credentials
ConnectionOptions options = new ConnectionOptions();
options.Username = this.Server.LoginUsername;
options.Password = this.Server.LoginPassword;
scope = new ManagementScope("\\\\" + this.Server.ServerName + "\\root\\cimv2", options);
}
else
{
// Local machine, no need for credentials
scope = new ManagementScope("\\\\" + this.Server.ServerName + "\\root\\cimv2");
}
return scope;
}
public void UpdateServicesList()
{
// Connect our scope to the actual WMI scope
this.Scope.Connect();
List<StringBuilder> servicesList = new List<StringBuilder>();
// Query system for Services
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Service WHERE Caption LIKE 'xxx%'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(this.Scope, query);
ManagementObjectCollection services = searcher.Get();
if (services.Count > 0)
{
foreach (ManagementObject queryObj in services)
{
StringBuilder s = new StringBuilder();
s.Append(queryObj["Caption"].ToString());
s.Append(",");
s.Append(queryObj["State"].ToString());
s.Append(",");
s.Append(queryObj["ProcessId"].ToString());
s.Append(";");
servicesList.Add(s);
}
}
}
Edit: Added code, thought I was doing right by keeping it simple!
So when I run this.UpdateServicesList(); in the constructor of the ServerConnectionTools class, I need to update the List in the Server class.
Edit2: While typing this I had a brainwave... Return a list of services from the update function and call the update function from the first class. Think this is a better approach?
What is the best approach to doing this?
Apologies for what is probably a simple question...
Cheers
Dave
Edit: Removed code snippet after your edits.
Yes, return a list of services. That's much better than sending in the parent class to the helper class.
You don't appear to be using the server that you send into the helper class. So you can just fetch the services from the update function. I would rename the update function to FetchServices or something instead of update.
public Server(string serverName, string loginUsername, string loginPassword)
{
this.ServerName = serverName;
this.LoginUsername = loginUsername;
this.LoginPassword = loginPassword;
// Login to server and retrieve list of services
ConnectionTools = new ServerConnectionTools(this);
this.Services = ConnectionTools.FetchServiceTools();
}
Use delegates.
Like this:
// Delegate Specification
public class MyClass
{
// Declare a delegate that takes a single string parameter
// and has no return type.
public delegate void LogHandler(string message);
// The use of the delegate is just like calling a function directly,
// though we need to add a check to see if the delegate is null
// (that is, not pointing to a function) before calling the function.
public void Process(LogHandler logHandler)
{
if (logHandler != null)
{
logHandler("Process() begin");
}
if (logHandler != null)
{
logHandler ("Process() end");
}
}
}
// Test Application to use the defined Delegate
public class TestApplication
{
// Static Function: To which is used in the Delegate. To call the Process()
// function, we need to declare a logging function: Logger() that matches
// the signature of the delegate.
static void Logger(string s)
{
Console.WriteLine(s);
}
static void Main(string[] args)
{
MyClass myClass = new MyClass();
// Crate an instance of the delegate, pointing to the logging function.
// This delegate will then be passed to the Process() function.
MyClass.LogHandler myLogger = new MyClass.LogHandler(Logger);
myClass.Process(myLogger);
}
}
In your situation you don't need whole service class in serviceConnectionTools.
here is my suggestion to you
Step 1 add an interface
interface caller
{
void setList(List<StringBuilder> par_list);
}
Step 2 implement the interface
public class Server : caller
{
private string m_serverName;
private string m_loginUsername;
private string m_loginPassword;
private ServerConnectionTools m_connectionTools;
public List<Service> Services { get; private set; }
public Server(string serverName, string loginUsername, string loginPassword)
{
this.ServerName = serverName;
this.LoginUsername = loginUsername;
this.LoginPassword = loginPassword;
// Login to server and retrieve list of services
ConnectionTools = new ServerConnectionTools(this);
}
public void setList(List<StringBuilder> par_list)
{
//do whatever you need to here.
}
}
step 3
public class ServerConnectionTools // Class 2
{
private caller m_serverCB;
private ManagementScope m_scope;
public ServerConnectionTools(caller par_serverCB)
{
m_serverCB = par_serverCB;
this.Scope = InitiateScope();
try
{
this.UpdateServicesList();
}
catch (System.Runtime.InteropServices.COMException)
{
// Server is unavailable
Console.WriteLine("Unable to reach server {0}", server.ServerName);
}
}
/// in your Updateservicelist function
public void UpdateServicesList()
{
// Connect our scope to the actual WMI scope
this.Scope.Connect();
List<StringBuilder> servicesList = new List<StringBuilder>();
// Query system for Services
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Service WHERE Caption LIKE 'xxx%'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(this.Scope, query);
ManagementObjectCollection services = searcher.Get();
if (services.Count > 0)
{
foreach (ManagementObject queryObj in services)
{
StringBuilder s = new StringBuilder();
s.Append(queryObj["Caption"].ToString());
s.Append(",");
s.Append(queryObj["State"].ToString());
s.Append(",");
s.Append(queryObj["ProcessId"].ToString());
s.Append(";");
servicesList.Add(s);
}
}
m_serverCB.setList(servicesList);//add this to call method.
}
Now you don't have to send all server object to class2. You can use serverConnectionTools for other classed you may need in the future instead of just using only one class.You have better oop implementation now I hope.
Related
I have a 3rd party scripting engine contained in a session in my code. The engine takes any delegate and makes it available to it's script with the same signature.
Now I want to have plugins that provide these delegates for the engine, but I also want extra data from the session without it showing up in the script.
The script consuming the delegate should have no idea about the session, but the plugin implementing it does. The plugin writer should be free to use any number or types of arguments for the plugin delegates, so I need to do this dynamically at run time.
For example:
//from available plugin delegates
delegate bool SendMessage(Session info, string ip, int port, string message);
delegate void LogMessage(Session info, string message);
//to create script delegates
delegate bool SendMessage(string ip, int port, string message);
delegate void LogMessage(string message);
So when the script engine calls LogMessage("Test") it should invoke LogMessage(mysession, "Test") in the plugin.
I found information on curry for adding defaults to delegates and Reflection could create the delegates, but how can they be fit together to accomplish this?
EDIT: full length example
public class Session
{
//Some metadata here
}
public class Plugin
{
private delegate bool SendMessage(Session info, string ip, int port, string message);
private delegate void LogMessage(Session info, string message);
public Delegate[] GetFunctions()
{
return new Delegate[] { new SendMessage(HandleSendMessage), new LogMessage(HandleLogMessage) };
}
private bool HandleSendMessage(Session info, string ip, int port, string message)
{
Console.WriteLine($"SEND {ip}:{port} >> \"{message}\"");
return true;
}
private void HandleLogMessage(Session info, string message)
{
Console.WriteLine($"LOG \"{message}\"");
}
}
//stand-in for 3rd party code
public class Engine
{
private IEnumerable<Delegate> _functions = null;
public void Add(IEnumerable<Delegate> functions)
{
//ignore this code, just simulating 3rd party behavior
_functions = functions;
}
public void Execute()
{
//ignore this code, just simulating 3rd party behavior
foreach (Delegate function in _functions)
{
ParameterInfo[] fparams = function.Method.GetParameters();
int n = fparams.Count();
object[] args = new object[n];
for (int i = 0; i < n; i++)
{
if (string.Compare(fparams[i].Name, "ip") == 0)
{
args[i] = "127.0.0.1";
}
else if (string.Compare(fparams[i].Name, "port") == 0)
{
args[i] = 80;
}
else if (string.Compare(fparams[i].Name, "message") == 0)
{
args[i] = "Some message";
}
else if (string.Compare(fparams[i].Name, "info") == 0)
{
Console.WriteLine("Error this should not be here");
args[i] = null;
}
}
function.DynamicInvoke(args);
}
}
}
class Program
{
static void Main(string[] args)
{
Plugin p = new Plugin(); //assume this instead comes from Assembly.Load(..) and Activator.CreateInstance(..)
Engine e = new Engine(); //stand-in for 3rd party code
List<Delegate> newDelegates = new List<Delegate>();
foreach (Delegate d in p.GetFunctions())
{
//QUESTION: create a new delegate same as (d) minus the first param (Session info)
//QUESTION: link the new delegate to (d) and set (Session info) to some value
newDelegates.Add(d); //add new delegate instead of (d)
}
e.Add(newDelegates);
e.Execute();
}
}
EDIT 2: Progress update
I can now create a delegate type with less variables then the original
/// <summary>
/// Based on code from user svick [https://stackoverflow.com/questions/9505117/creating-delegates-dynamically-with-parameter-names]
/// </summary>
class DelegateTypeFactory
{
private readonly ModuleBuilder _module;
public DelegateTypeFactory()
{
//Build in-memory assembly to contain the new types
AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("DelegateTypeFactory"), AssemblyBuilderAccess.RunAndCollect);
_module = assembly.DefineDynamicModule("DelegateTypeFactory");
}
public Type CreateDelegateType(MethodInfo method)
{
//Create new name for the type to avoid clashes
string nameBase = string.Format("{0}{1}", method.DeclaringType.Name, method.Name);
string name = GetUniqueName(nameBase);
//Create the toolset to make the new type
TypeBuilder builder = _module.DefineType(name, TypeAttributes.Sealed | TypeAttributes.Public, typeof(MulticastDelegate));
ConstructorBuilder constructor = builder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(object), typeof(IntPtr) });
constructor.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
//define the methods params and filter unwanted param
ParameterInfo[] parameters = method.GetParameters();
parameters = parameters.Where(p => p.ParameterType != typeof(Session)).ToArray();
//design the method signature
MethodBuilder invokeMethod = builder.DefineMethod("Invoke", MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Public, method.ReturnType, parameters.Select(p => p.ParameterType).ToArray());
invokeMethod.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
for (int i = 0; i < parameters.Length; i++)
{
invokeMethod.DefineParameter(i + 1, ParameterAttributes.None, parameters[i].Name);
}
//Return the newly created delegate type
return builder.CreateType();
}
private string GetUniqueName(string nameBase)
{
int number = 2;
string name = nameBase;
while (_module.GetType(name) != null)
{
name = $"{nameBase}{number++}";
}
return name;
}
}
Usage:
DelegateTypeFactory factory = new ConsoleApplication1.DelegateTypeFactory();
Type newDelegateType = factory .CreateDelegateType(originalDelegate.Method);
However how one could instantiating the new delegate and make it call the original delegate with the default session value eludes me
It seems like you have Plugins passing delegates into the Engine.
The engine then invokes the plugins dynamically.
You can do this with closures, but the plugin would have to create the closure since it is creating the delegate.
So 3rd party developers could use this technique as well, it would be up to them. If they don't need any extra objects available in the delegate they don't have to.
It would be transparent to the Engine that the delegate has captured other variables.
I see in your main you have comments that indicate you're thinking about mutating the plugin functions there.
I don't know how you would do it there since you wouldn't know what paramaters the Plugin author intended to be in/visible.
So I wrote this to allow the Plugin to decide what it wants to hide.
I left your Handle* methods the way you wrote them, but they do have access to the Session objects if required.
public class Session
{
//Some metadata here
}
public class Plugin
{
private delegate bool SendMessage(string ip, int port, string message);
private delegate void LogMessage(string message);
public Delegate[] GetFunctions()
{
var sessionInfo = new Session();
return new Delegate[] { new SendMessage(HandleSendMessage(sessionInfo)), new LogMessage(HandleLogMessage(sessionInfo)) };
}
private SendMessage HandleSendMessage(Session info)
{
return delegate (string ip, int port, string message)
{
Console.WriteLine($"SEND {ip}:{port} >> \"{message}\"");
return true;
};
}
private LogMessage HandleLogMessage(Session info)
{
return delegate (string message)
{
Console.WriteLine($"LOG \"{message}\"");
};
}
}
//stand-in for 3rd party code
public class Engine
{
private IEnumerable<Delegate> _functions = null;
public void Add(IEnumerable<Delegate> functions)
{
//ignore this code, just simulating 3rd party behavior
_functions = functions;
}
public void Execute()
{
//ignore this code, just simulating 3rd party behavior
foreach (Delegate function in _functions)
{
ParameterInfo[] fparams = function.Method.GetParameters();
int n = fparams.Count();
object[] args = new object[n];
for (int i = 0; i < n; i++)
{
if (string.Compare(fparams[i].Name, "ip") == 0)
{
args[i] = "127.0.0.1";
}
else if (string.Compare(fparams[i].Name, "port") == 0)
{
args[i] = 80;
}
else if (string.Compare(fparams[i].Name, "message") == 0)
{
args[i] = "Some message";
}
else if (string.Compare(fparams[i].Name, "info") == 0)
{
Console.WriteLine("Error this should not be here");
args[i] = null;
}
}
function.DynamicInvoke(args);
}
}
}
class Program
{
static void Main(string[] args)
{
Plugin p = new Plugin(); //assume this instead comes from Assembly.Load(..) and Activator.CreateInstance(..)
Engine e = new Engine(); //stand-in for 3rd party code
List<Delegate> newDelegates = new List<Delegate>();
foreach (Delegate d in p.GetFunctions())
{
//QUESTION: create a new delegate same as (d) minus the first param (Session info)
//QUESTION: link the new delegate to (d) and set (Session info) to some value
newDelegates.Add(d); //add new delegate instead of (d)
}
e.Add(newDelegates);
e.Execute();
}
}
I have a WCF message inspector which inspects requests and responses: Message. The inspector works fine. A Message object can only be read once so once you read it, you cannot simply propagate as WCF will complain that the message has been read. Therefore, I am creating a brand new copy of the message and propagating that.
I have designed a class that allows message reading and after the caller has read whatever they want, they need to call Close which will return a copy of the message. Here is the skeleton of my class:
using System.ServiceModel.Channels;
internal abstract class MessageReader
{
internal string ReadSomething(string id)
{
// Return string
}
internal string ReadSomethingElse(string id)
{
// Return string
}
internal Message Close()
{
// Create copy and return it.
}
}
Users of my class may forget to call Close() which is fine because WCF will yell at them. Right now I have documentation to let users know they need to call Close().
Here is the question
Is there a pattern, or something similar, to C#'s using construct but one which returns an object at the end? This will be really convenient because then users of my class can just use a construct like that and at the end it will return the copy of the message. Something like this:
UsingSomeConstruct(var reader = new MessageReader(ref originalMessage))
{
var a = reader.ReadSomething("something");
var b = reader.ReadSomethingElse("something");
// Do something with what was read
}
// At this point originalMessage will be the copy of the message and no longer the original message.
EDIT
I thought about hacking IDisposable to achieve this but I am NOT going to do it that way so looking for other ideas.
There is no such language construct of course.
What I could suggest is to use IDisposable for cleaning up, and add ref Message message argument to each ReadXXX method. I know it will not be so convenient for your users, but from the other side they cannot forget passing the parameter.
So the implementation would be something like this:
internal class MessageReader : IDisposable
{
private MessageBuffer buffer;
private Message message;
private void Release()
{
if (buffer == null) return;
buffer.Close();
buffer = null;
message = null;
}
protected void OnReadRequest(ref Message message)
{
if (message == null) throw new ArgumentNullException("message");
if (this.message == message) return;
Release();
this.buffer = message.CreateBufferedCopy(int.MaxValue);
message = this.message = buffer.CreateMessage();
}
public void Dispose()
{
Release();
}
internal string ReadSomething(ref Message message, string id)
{
OnReadRequest(ref message);
// Return string
}
internal string ReadSomethingElse(ref Message message, string id)
{
OnReadRequest(ref message);
// Return string
}
}
and the sample usage:
using (var reader = new MessageReader())
{
var a = reader.ReadSomething(ref originalMessage, "something");
var b = reader.ReadSomethingElse(ref originalMessage, "something");
// Do something with what was read
}
// At this point originalMessage will be the copy of the message and no longer the original message.
The way I'd do this is as follows:
public MessageReader: IDisposable
{
public static MessageReader Create(ref Message message)
{
var buffer = message.CreateBufferedCopy(/*whatever is fit*/);
try
{
var reader = new MessageReader(buffer);
message = buffer.CreateMessage();
return reader;
}
catch
{
buffer.Close();
throw;
}
}
private readonly MessageBuffer buffer;
private bool disposed;
private MessageReader(MessageBuffer buffer) { this.buffer = buffer; }
public void Dispose()
{
if (disposed)
return;
buffer.Close();
disposed = true;
}
public string Read(string id)
{
var newCopy = buffer.CreateMessage();
//work with new copy...
}
}
And you'd simply use it like this:
using (var reader = MessageReader.Create(ref message))
//message here is already an untouched copy with no need of user active
//intervention and is never touched again by the reader.
{
var a = reader.Read("something"); //reads copy
...
}
IMHO, this is as clean as it can be. Note that MessageReader implements IDisposable exclusively because it holds a reference to the disposable private MessageBuffer.
Thanks to all the help from #InBetween, #quetzalcoatl, and #Ivan Stoev. Upvoted your answers because it helped me arrive at the following.
In the constructor, I create a copy of the message and set the original message to the copy. Since the status of this message is Created WCF will be happy propogating it. I create another copy and use that for reading multiple times.
#Ivan said but what if the user does not ask for anything to be read then the copying was wasted work. That is a good point but in my case, this is an interceptor and all messages are intercepted to be read.
Here is the code I ended up with suggestions from all of you:
public class MessageReader : IDisposable {
private readonly Message message;
public MessageReader(ref Message originalMessage) {
using( var buffer = originalMessage.CreateBufferedCopy( int.MaxValue ) ) {
// Keep original message for reading
this.message = buffer.CreateMessage();
// Set original message to a copy of the original
originalMessage = buffer.CreateMessage();
}
}
public int ReadSomething(string id) {
// Read from this.message;
}
public int ReadSomethingElse(string id) {
// Read from this.message;
}
public void Close() {
this.Dispose();
}
public void Dispose() {
this.message.Close();
}
}
The caller can either use it in a using block or without it. The using block is used for good reasons and not as a hack.
public object AfterReceiveRequest(ref Message request, IClientChannel channel,
InstanceContext instanceContext) {
try {
using( var rdr = new MessageReader(ref request) ) {
var value= rdr.ReadSomething( someIdentifier );
return value;
}
}
catch( System.Exception ex ) {
throw CreateFault( ex, request );
}
}
Nope, there is no such construct. It is simply too specific to exist there out of the box. There are extension methods which often are very helpful, but you won't be able to use them on this ref Message parameter..
However, if you are willing to use ref at all, then why dont simply include all that logic it in Reader's constructor?
Here's an example, somewhat contrived, but it should show what I mean. Like others mentioned in comments, I also suggest implementing IDisposable on the Reader object instead of Close, so I included that already.
TL;DR: In example below, the most important thing is in Reader(ref msg) constructor which clones the message, copies the data, and replaces the original message with a safe-message class which can be read many times..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace Rextester
{
public class Program
{
public static void Main(string[] args)
{
// real-world variables, keep them typed as base Message
// to be able to silently replace them with different objects
Message original1;
Message original2;
// let's construct some one-time readable messages
{
var tmp1 = new OneTimeMessage();
tmp1.data["mom"] = "dad";
tmp1.data["cat"] = "dog";
original1 = tmp1;
var tmp2 = new OneTimeMessage();
tmp2.data["mom"] = "dad";
tmp2.data["cat"] = "dog";
original2 = tmp2;
}
// test1 - can't read twice
Console.WriteLine("test0A:" + original1.GetData("mom"));
//Console.WriteLine("test0B:" + original1.GetData("mom")); // fail
// test2 - can read twice with Reader's help
var backup1 = original2;
using(var rd1 = new Reader(ref original2))
{
Console.WriteLine("test1A:" + rd1.ReadSomething("mom"));
}
var backup2 = original2;
using(var rd2 = new Reader(ref original2))
{
Console.WriteLine("test1A:" + rd2.ReadSomething("mom"));
//^ ok - becase Reader replaced 'original2' with SafeMessage
}
// test3: Reader's ctor is intelligent
// so no more SafeMessages created during future usage
var backup3 = original2;
using(var rd3 = new Reader(ref original2))
{
}
var backup4 = original2;
using(var rd4 = new Reader(ref original2))
{
}
Console.WriteLine("checking for copies:" + (original2 == backup1));
Console.WriteLine("checking for copies:" + (original2 == backup2));
Console.WriteLine("checking for copies:" + (original2 == backup3));
Console.WriteLine("checking for copies:" + (original2 == backup4));
}
}
}
public abstract class Message
{
public abstract string GetData(string id);
}
public class OneTimeMessage : Message // this models your current one-time-readable message
{
public IDictionary<string, string> data = new Dictionary<string, string>();
public override string GetData(string id)
{
var tmp = data[id];
data.Remove(id);
// that's nonsense, but I want to show that you can't
// read the same thing twice from this object
return tmp;
}
}
public class SafeMessage : Message
{
public IDictionary<string, string> data;
public override String GetData(string id)
{
return data[id];
}
public SafeMessage(Message msg)
{
// read out the full msg's data and store it
// since this is example, we can do it in a pretty simple way
// in your code that will probably be more complex
this.data = new Dictionary<string,string>(((OneTimeMessage)msg).data);
}
}
public class Reader : IDisposable
{
private Message message;
public Reader(ref Message src)
{
src = src is SafeMessage ? src : new SafeMessage(src);
this.message = src;
}
public string ReadSomething(string id){ return message.GetData(id); }
public void Dispose(){ Close(); }
public void Close(){ message=null; Console.WriteLine("reader closed"); }
}
EDIT: improved example
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Channels;
using System.Text.RegularExpressions;
using System.Xml;
namespace MyProgram
{
public class Program
{
public static void Main(string[] args)
{
// real-world variables, keep them typed as base Message
// to be able to silently replace them with different objects
Message original1;
Message original2;
// let's construct some one-time readable messages
{
original1 = new TheMessage("dad", "dog");
original2 = new TheMessage("dad", "dog");
}
// test1 - can't read twice
Console.WriteLine("test0A:" + original1.GetReaderAtBodyContents().ReadOuterXml());
// Console.WriteLine("test0B:" + original1.GetReaderAtBodyContents().ReadOuterXml()); // fail: InvalidOperationException - it was already read
// test2 - can read ONCE with Reader's help, but the message is replaced and is usable again
var backup1 = original2;
using (var rd1 = new ReaderOnce(ref original2))
{
Console.WriteLine("is message replaced after opening Reader:" + (original2 != backup1));
Console.WriteLine("test1A:" + rd1.ReadBodyXml());
// Console.WriteLine("test1B:" + rd1.ReadBodyXml()); // fail: InvalidOperationException - it was already read
}
// test3 - can read MANY TIMES with ReaderMany's help
// also note we use 'original2' again, which was already used above, so in fact ReaderOnce really works as well
var backup2 = original2;
using (var rd1 = new ReaderMany(ref original2))
{
Console.WriteLine("is message replaced after opening Reader:" + (original2 != backup2));
Console.WriteLine("test2A:" + rd1.ReadBodyXml());
Console.WriteLine("test2B:" + rd1.ReadBodyXml()); // ok
}
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
}
}
// solution1
public class ReaderOnce : IDisposable
{
private Message localCopy;
public ReaderOnce(ref Message src)
{
// create a WCF MessageBuffer to assist in copying messages
// btw. I suppose you should set some sane limit instead of that below
using (var tempBuffer = src.CreateBufferedCopy(int.MaxValue))
{
src = tempBuffer.CreateMessage(); // FIRST copy for outer use
localCopy = tempBuffer.CreateMessage(); // SECOND copy for internal use in the Reader
}
}
public void Dispose() { Close(); }
public void Close()
{
localCopy.Close(); // but that does NOT affect FIRST copy sent to outer scope outside reader
Console.WriteLine("reader closed");
}
public string ReadBodyXml() // careful: that's again ONE TIME readable
{
return localCopy.GetReaderAtBodyContents().ReadOuterXml();
}
}
// solution2
public class ReaderMany : IDisposable
{
private MessageBuffer localBuffer;
public ReaderMany(ref Message src)
{
localBuffer = src.CreateBufferedCopy(int.MaxValue);
src = localBuffer.CreateMessage(); // FIRST copy for outer use
}
public void Dispose() { Close(); }
public void Close()
{
localBuffer.Close();
Console.WriteLine("reader closed");
}
public string ReadBodyXml() // this is readable multiple times
{
using (var tmp = localBuffer.CreateMessage())
return tmp.GetReaderAtBodyContents().ReadOuterXml();
}
}
// let's fake some Message type to have something to test the Reader on
public class TheMessage : Message
{
public override MessageHeaders Headers => _mh;
public override MessageProperties Properties => _mp;
public override MessageVersion Version => _mv;
private MessageHeaders _mh;
private MessageProperties _mp;
private MessageVersion _mv;
private string data1;
private string data2;
// btw. below: surprise! XmlDictionaryWriter is in "System.Runtime.Serialization", not in "System.Xml"
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement("foo");
writer.WriteAttributeString("data1", data1);
writer.WriteAttributeString("data2", data2);
writer.WriteEndElement();
}
public TheMessage(string data1, string data2)
{
// remember, this class is just an example, you will work on your own messages you already have
_mv = MessageVersion.Soap12;
_mh = new MessageHeaders(_mv);
_mp = new MessageProperties();
// below: yeah, that's super-naive and wrong, but that's an example
this.data1 = data1;
this.data2 = data2;
}
}
There is no language construct in c# that does what you are asking. As stated in comments, you could abuse IDisposable and the language and use a using block to achieve what you want.
But, I fail see what you are gaining, you are just punting the problem; now users will need to remember to use usinginstead of Close. The latter is simple and clean, the former uses a very known language construct to do something different to what it was thought for, something that will potentially be very confusing.
I am using a serial port object that was generated by the designer in C# (non static).
I need to be able to access it from methods that are static in different classes (I know it is a bad practice but that is what I inherit)
The port access use the below code.
public bool Read_Board_Port()
{
byte[] bData = new byte[256];
string message;
bool sucess = false;
try
{
if (!(serialBoardPort.IsOpen == true))
Connect_To_Board(Globals.BoardportName, Globals.BoardbaudRate, Globals.Boardparity, Globals.BoardstopBits, Globals.BoarddataBits);
if(CMDDirect || Globals.HostCommandString)
{
serialBoardPort.ReadTimeout = 1000; // Timeout if no answer from the port.
message = serialBoardPort.ReadLine();
Globals.RXBoardBuff = Encoding.UTF8.GetBytes(message);
Write_To_Console_Dr(message);
sucess = true;
}
else
{
serialBoardPort.Read(Globals.RXBoardBuff, 0, Constants.RXBOARDBUFFSIZE);
if (Check_Command_Correct(Globals.RXBoardBuff, Globals.CommandOut))
sucess = true;
else
{
Write_Error_To_Console_Dr(Constants.ERRORDATAFROMBOARDPORT);
sucess = false;
}
}
}
catch
{
MessageBox.Show(Constants.ERRORNODATABOARPORT);
sucess = false;
}
return sucess;
}
If I declare new a different instance of the serial port will be used, I need to use the port that is already open.
Thanks
As stated by #Matthew Spencer you should pass the serial port as a parameter to the static methods that needs it. First create a method on your board class or whatever its name is that returns the instance of your serial port. Then use it to get the serial port for use to the static methods you mentioned.
Something like this should be what you need..
public bool Read_Board_Port()
{
byte[] bData = new byte[256];
string message;
bool sucess = false;
try
{
if (!(serialBoardPort.IsOpen == true))
Connect_To_Board(Globals.BoardportName, Globals.BoardbaudRate, Globals.Boardparity, Globals.BoardstopBits, Globals.BoarddataBits);
if(CMDDirect || Globals.HostCommandString)
{
serialBoardPort.ReadTimeout = 1000; // Timeout if no answer from the port.
message = serialBoardPort.ReadLine();
Globals.RXBoardBuff = Encoding.UTF8.GetBytes(message);
Write_To_Console_Dr(message);
sucess = true;
}
else
{
serialBoardPort.Read(Globals.RXBoardBuff, 0, Constants.RXBOARDBUFFSIZE);
if (Check_Command_Correct(Globals.RXBoardBuff, Globals.CommandOut))
sucess = true;
else
{
Write_Error_To_Console_Dr(Constants.ERRORDATAFROMBOARDPORT);
sucess = false;
}
}
}
catch
{
MessageBox.Show(Constants.ERRORNODATABOARPORT);
sucess = false;
}
return sucess;
}
// since serialBoardPort seems to be a globally declared variable
public SerialPort GetInstance()
{
return serialBoardPort;
}
// Let's name your class as board..
// on somewhere in your app code:
Board board = // GetValue
SerialPort boardSerialPort = board.GetInstance();
ClassXXX.StaticMethodNeedsPort(boardSerialPort); // pass your serial port to the static method
UPDATE: Since there was a bit of misunderstanding as the questioner said..
I suggest using an IoC container, read more here
Here is what I use. Normally this is already a part of frameworks such as MVVM Cross.
CODE:
public class Core
{
private static readonly Core instance = new Core();
private Dictionary<Type, object> container;
private Core()
{
container = new Dictionary<Type, object>();
}
public void RegisterSingleton<T>(T value) where T : class
{
Type type = typeof(T);
if (!container.ContainsKey(type))
container.Add(type, value);
}
public T GetSingleton<T>() where T : class
{
Type type = typeof(T);
if (container.ContainsKey(type))
return (T)container[type];
else
throw new Exception("Singleton instance not registered.");
}
public void RemoveSingleton<T>() where T : class
{
Type type = typeof(T);
if (container.ContainsKey(type))
container.Remove(type);
}
public void ClearSingletons()
{
container.Clear();
}
public static Core Instance
{
get { return instance; }
}
}
When your application loads add this line:
Core.Instance.ClearSingletons();
In case it already has a port upon loading since it is auto-generated by C# just register the instance too..
Core.Instance.RegisterSingleton(MySerialPortObject); // Or class. Can be object
On the part of the application when you need the port just get its instance like this...
SerialPort _myPort = Core.Instance.GetSingleton<X>(); // Where X value is the type of your registered object. If you are registering a SerialPort then replace X with SerialPort.
You can get the instance of your port anywhere you like. When I use this I normally register implementation of interfaces so that I can get it like
IFileHandler _fileHandler = Core.Instance.GetSingleton<IFileHandler>() // Where I registered the class that implements IFileHandler upon the startup of my application
Sorry for the long answer.
Ninject doesn’t provide a InSessionScope Binding for Websites, so we have created our own extension:
public static IBindingNamedWithOrOnSyntax<T> InSessionScope<T>(this IBindingInSyntax<T> parent)
{
return parent.InScope(SessionScopeCallback);
}
private const string _sessionKey = "Ninject Session Scope Sync Root";
private static object SessionScopeCallback(IContext context)
{
if (HttpContext.Current.Session[_sessionKey] == null)
{
HttpContext.Current.Session[_sessionKey] = new object();
}
return HttpContext.Current.Session[_sessionKey];
}
This extension is working fine until we are using the standard local SessionStore.
But we changed the SessionStore and we now use the „AppFabricCacheSessionStoreProvider“ and this store is no longer on the local machine its on the server.
And the problem is that Ninject tries to resolve the reference of an object which was serialized and deserialized and comes from the server and not from the local memory and so ninject can’t find the reference. The result is, that ninjects allways creates a new Object and the SessionScope does not work any more.
Edit 1:
We are using this functionality
https://msdn.microsoft.com/en-us/library/hh361711%28v=azure.10%29.aspx
and here I can use the standard "HttpContext.Current.Session" Object and the list content is stored on the server and not on the local machine.
So architecturally you have a problem in that you need to store the settings for AppFabric somewhere, and this is an issue with your static method. But assume you create a public static class like so:
public static class AppCache
{
public static DataCache Cache { get; private set; }
static AppCache()
{
List<DataCacheServerEndpoint> servers = new List<DataCacheServerEndpoint>(1);
servers.Add(new DataCacheServerEndpoint("ServerName", 22233)); //22233 is the default port
DataCacheFactoryConfiguration configuration = new DataCacheFactoryConfiguration
{
Servers = servers,
LocalCacheProperties = new DataCacheLocalCacheProperties(),
SecurityProperties = new DataCacheSecurity(),
RequestTimeout = new TimeSpan(0, 0, 300),
MaxConnectionsToServer = 10,
ChannelOpenTimeout = new TimeSpan(0, 0, 300),
TransportProperties = new DataCacheTransportProperties() { MaxBufferSize = int.MaxValue, MaxBufferPoolSize = long.MaxValue }
};
DataCacheClientLogManager.ChangeLogLevel(System.Diagnostics.TraceLevel.Off);
var _factory = new DataCacheFactory(configuration);
Cache = _factory.GetCache("MyCache");
}
}
then you can change extension like so:
public static IBindingNamedWithOrOnSyntax<T> InSessionScope<T>(this IBindingInSyntax<T> parent)
{
return parent.InScope(SessionScopeCallback);
}
private const string _sessionKey = "Ninject Session Scope Sync Root";
private static object SessionScopeCallback(IContext context)
{
var cachedItem = AppCache.Cache.Get("MyItem"); // IMPORTANT: For concurrency reason, get the whole item down to method scope.
if (cachedItem == null)
{
cachedItem = new object();
AppCache.Cache.Put("MyItem", cachedItem);
}
return cachedItem;
}
I've found a "Solution" that works so far it's not perfect because I am avoiding the AppFabric Store with an Localstore for the Object Reference.
public static IBindingNamedWithOrOnSyntax<T> InSessionScope<T>(this IBindingInSyntax<T> parent)
{
return parent.InScope(SessionScopeCallback);
}
public static Dictionary<string, object> LocalSessionStore = new Dictionary<string, object>();
private const string _sessionKey = "Ninject Session Scope Sync Root";
private static object SessionScopeCallback(IContext context)
{
var obj = new object();
var key = (string)HttpContext.Current.Session[_sessionKey];
if (string.IsNullOrEmpty(key))
{
var guid = Guid.NewGuid().ToString();
HttpContext.Current.Session[_sessionKey] = guid;
LocalSessionStore.Add(guid, obj);
}
else if(!LocalSessionStore.ContainsKey(key))
{
LocalSessionStore.Add(key, obj);
return LocalSessionStore[key];
}
else if (LocalSessionStore.ContainsKey(key))
{
return LocalSessionStore[key];
}
return HttpContext.Current.Session[_sessionKey];
}
}
I'm using RedditSharp from https://github.com/SirCmpwn/RedditSharp in a script of mine, and I'm simply asking, when connecting using this how do I implement a proxy? and could I change the proxy midscript?
There no standalone way, you can't accomplish this without modifying this library source code.
So most painless-way:
Overload constructor of RedditSharp - add new argument with IWebAgent as type. So it will look like this:
public Reddit() : this(new WebAgent())
{
}
public Reddit(IWebAgent agent)
{
JsonSerializerSettings = new JsonSerializerSettings();
JsonSerializerSettings.CheckAdditionalContent = false;
JsonSerializerSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
_webAgent = agent;
CaptchaSolver = new ConsoleCaptchaSolver();
}
Remove "sealed" keyword from RedditSharp.WebAgent class declaration.
Make RedditSharp.WebAgent.CreateRequest method virtual, so it will look like this:
public virtual HttpWebRequest CreateRequest(string url, string method, bool prependDomain = true)
{
...
}
Create your own WebAgent based on old-one:
public class MyAgent: WebAgent
{
public IWebProxy Proxy { get; set; }
public override HttpWebRequest CreateRequest(string url, string method, bool prependDomain = true)
{
var base_request = base.CreateRequest(url, method, prependDomain);
if (Proxy != null)
{
base_request.Proxy=Proxy;
}
return base_request;
}
}
Use it in your code:
var agent = new MyAgent();
var reddit = new Reddit(agent);
...
agent.Proxy = new WebProxy("someproxy.net", 8080);
So now you can set proxy anytime, from anywhere. Not tested really, but it must work.