Instrument a methods to intercept each method's call - c#

I have the need (a new requirements) to perform few action before and after each method call of a bunch of classes in my project. Is a situation that somewhat reassemble a AOP but what i need is not that complex. I have some BusinessObject (class) and need that each method invokation of my API contained in that BOs will handle logging and authoriting. Because i would avoid to rewrite my codebase i have thougt to wrap my BO, in that way i can "instrument" them, but i am not sure if this is the best approach nor how to deal with some details
Example :
public class TestBo : BaseBo
{
private string test = "";
public TestBo ( string input )
{
test = input;
}
[PermissionRequired(PermissionAttribute.Login | PermissionAttribute.Read)]
public void ExampleMethod ()
{
Console.WriteLine("Run it with " + test);
}
public void WriteMessage ( string message )
{
Console.WriteLine("Write it " + message);
}
}
// IN THE BO Wrapper Class
public virtual OperationResultDto<object> Execute(Action action, Token token)
{
return this.ActionExecute(action, token);
}
public virtual OperationResultDto<object> Execute<T1>(Action<T1> action, Token token , T1 param)
{
return this.ActionExecute(action, token, param);
}
protected virtual OperationResultDto<object> ActionExecute (Delegate action, Token token, params object[] parameters)
{
try
{
if (action == null)
throw new ArgumentNullException("action");
this._logger.Debug(this.DumpParameters(action, parameters));
PermissionRequired permission = action.Method.GetCustomAttributes(false).FirstOrDefault(attr => attr is PermissionRequired) as PermissionRequired;
//TODO : authorization;
action.DynamicInvoke(parameters);
this._logger.Debug($"Invokation {action.Method.Name} ended");
return new OperationResultDto<object>() { Success = true, ReturnData = null };
}
catch (Exception err)
{
this._logger.Error(err, string.Empty);
return new OperationResultDto<object>()
{
Success = false,
ErrorText = err.Message,
Error = err,
ReturnData = null
};
}
return null;
}
//USAGE
TestBo testBo = new TestBo("Identity");
testBo.ExampleMethod();
Token token = new Token(1);
BoWrapper wrapper = new BoWrapper(testBo);
wrapper.Execute(testBo.ExampleMethod, token);
wrapper.Execute<string>(testBo.WriteMessage, token, "Message");
That seems to work as intended, but i don't like that :
wrapper.Execute<string>.. //I have to repeat method signature each time
public virtual OperationResultDto<object> Execute<T1>(Action<T1>..
//I have to write many overload (in BoWrapper) to handle all possible situation.

Related

How to wait for methods to finish then do new action?

I'm setting up my architechture to use Cef.Offscreen. In order to make it easy to work with I have divided some parts. But I run into a problem that controller loading finshes and serves a view before everything has been able to load.
Here's my structure --> Controller
public ActionResult InitBrowser()
{
ICefSharpRenderer renderer = RendererSingelton.GetInstance();
//Try to render something in default appdomain
renderer.LoginToTradingView(null, null);
ViewBag.SiteTitle = BrowserActions.RunScriptInNamedBrowser("loginbrowser", #"(function() {return document.title;} )();");
ViewBag.ImagesixtyfourUrl = BrowserActions.TakeScreenshot("loginbrowser");
//this is returned to fast, we have to wait for all
return View();
}
I have this class to get do some basic actions and initialize if needed.
public class CefSharpRenderer : MarshalByRefObject, ICefSharpRenderer
{
private ChromiumWebBrowser _browser;
private TaskCompletionSource<JavascriptResponse> _taskCompletionSource;
private string _name;
public void LoginToTradingView(string url, string browserName)
{
CheckIfCefIsInitialized();
BrowserFactory.GetBrowserInstance(#"https://se.tradingview.com/", "loginbrowser");
}
public void CreateBrowserAndGoToUrl(string url, string browserName)
{
CheckIfCefIsInitialized();
BrowserFactory.GetBrowserInstance(url, "browserName");
}
public void CheckIfCefIsInitialized()
{
if (!Cef.IsInitialized)
{
var settings = new CefSettings();
var assemblyPath = Path.GetDirectoryName(new Uri(GetType().Assembly.CodeBase).LocalPath);
settings.BrowserSubprocessPath = Path.Combine(assemblyPath, "CefSharp.BrowserSubprocess.exe");
settings.ResourcesDirPath = assemblyPath;
settings.LocalesDirPath = Path.Combine(assemblyPath, "locales");
var osVersion = Environment.OSVersion;
//Disable GPU for Windows 7
if (osVersion.Version.Major == 6 && osVersion.Version.Minor == 1)
{
// Disable GPU in WPF and Offscreen examples until #1634 has been resolved
settings.CefCommandLineArgs.Add("disable-gpu", "1");
}
//Perform dependency check to make sure all relevant resources are in our output directory.
Cef.Initialize(settings, performDependencyCheck: false, cefApp: null);
}
}
}
I get my browserinstance here and connected the events to be fired.
public static class BrowserFactory
{
public static ChromiumWebBrowser GetBrowserInstance(string _url, string browsername)
{
if (!BrowserContainer.CheckIfBrowserExists(browsername))
{
ChromiumWebBrowser _browser = new ChromiumWebBrowser(_url);
_browser.LoadingStateChanged += BrowserEvents.OnLoadingStateChanged;
BrowserContainer.AddDataHolder(browsername, new DataBrowserHolder { BrowserName = browsername, ChromiumWebBrow = _browser });
return _browser;
}
return null;
}
}
Browserevent loads correct page.
public static class BrowserEvents
{
public static void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs args)
{
if (args.IsLoading == false)
{
ChromiumWebBrowser cwb = (ChromiumWebBrowser)sender;
if (cwb.Address == "https://se.tradingview.com/")
{
BrowserActions.LogInToTradingView("xxxxx", "yyyyyyy", "loginbrowser");
}
}
}
}
Last my browseractions, spare med for the thread sleeps it's just under construction and it works atm.
public static class BrowserActions
{
public static void LogInToTradingView(string twusername, string twpassword, string browserName)
{
ChromiumWebBrowser _dataholder = BrowserContainer.GetDataHolderByName(browserName).ChromiumWebBrow;
IFrame ifww = _dataholder.GetMainFrame();
// var lull = #"(function() { var serielength = TradingView.bottomWidgetBar._widgets.backtesting._reportWidgetsSet.reportWidget._data.filledOrders.length; return serielength; })();";
// JavascriptResponse _js = Task.Run(async () => { return await _browser.GetMainFrame().EvaluateScriptAsync(lull); }).Result;
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-header__link tv-header__link--signin js-header__signin')[0].click();})();");
// var loginusernamescript =
var loginpasswordscript = #"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[1].value= " + twpassword + "; })();";
var clkloginbtn = #"(function() { document.getElementsByClassName('tv-button tv-button--no-border-radius tv-button--size_large tv-button--primary_ghost tv-button--loader')[0].click();})();";
Thread.Sleep(300);
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[0].click();})();");
Thread.Sleep(50);
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[0].value = '" + twusername + "';})();");
Thread.Sleep(50);
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[1].click();})();");
Thread.Sleep(50);
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[1].value = '" + twpassword + "';})();");
Thread.Sleep(50);
ifww.ExecuteJavaScriptAsync(#"(function() { document.getElementsByClassName('tv-button tv-button--no-border-radius tv-button--size_large tv-button--primary_ghost tv-button--loader')[0].click();})();");
}
public static string TakeScreenshot(string browserName)
{
try
{
Bitmap img = Task.Run(async () => { return await BrowserContainer.GetDataHolderByName(browserName).ChromiumWebBrow.ScreenshotAsync(); }).Result;
// object mgss = img.Clone();
string baseen = ExtraFunctions.ToBase64String(img, ImageFormat.Png);
return baseen;
}
catch (Exception e)
{
var x = e.InnerException;
return null;
}
}
public static string RunScriptInNamedBrowser(string browserName, string script)
{
try
{
string str = Task.Run(async () => { return await BrowserContainer.GetDataHolderByName(browserName).ChromiumWebBrow.GetMainFrame().EvaluateScriptAsync(script); }).Result.ToString();
// object mgss = img.Clone();
return str;
}
catch (Exception e)
{
var x = e.InnerException;
return null;
}
}
}
How can I get my browser actions to report back to my controller so that I can wait for them to finish?
For a Task asynchronous operation to report back, it's possible to use Progress<T>. How that's done is detailed in Enabling Progress and Cancellation in Async APIs. The key is:
var progressIndicator = new Progress<int>(ReportProgress);
This creates a Progress<T> object that can indicate how far a task is complete, and also call a custom method (ReportProgress) at set intervals. You can create a custom class if necessary instead of using int.
So your browser actions can report back to the controller with the progress reporting method until everything is complete.

C#: creating partial functions dynamically

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();
}
}

Is there a construct or pattern similar to C# `using` which will return an object?

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.

Async Functions making code not working

First, apologize me for the title... I haven't found any suiting my single case :P
I need to download an INI file to fill a Dictionary first of all. For this, I have this class:
public class Properties : INotifyPropertyChanged
{
private Dictionary<string, string> _properties;
public Properties()
{
_properties = new Dictionary<string, string>();
}
public async void Load(string uri)
{
Stream input = await connection(uri);
StreamReader rStream = new StreamReader(input);
string line;
while((line = rStream.ReadLine()) != null)
{
if(line != "")
{
int pos = line.IndexOf('=');
string key = line.Substring(0, pos);
string value = line.Substring(pos + 1);
_properties.Add(key, value);
Debug.WriteLine("Key: " + key + ", Value: " + value);
}
}
Debug.WriteLine("Properties dictionary filled with " + _properties.Count + " items.");
}
public async Task<Stream> connection(string uri)
{
var httpClient = new HttpClient();
Stream result = Stream.Null;
try
{
result = await httpClient.GetStreamAsync(uri);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
Debug.WriteLine(ex.HResult);
}
return result;
}
public string getValue(string key)
{
string result = "";
try
{
result = _properties[key];
}
catch(Exception ex)
{
Debug.WriteLine(ex.Message);
Debug.WriteLine(ex.HResult);
result = "Not found";
}
return result;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName]string propertyName = "")
{
var Handler = PropertyChanged;
if (Handler != null)
Handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Mainly, the Dictionary contains a Key and a URL to download XML files to each page of the application.
The MainPage, which is the one that is gonna be fill has this code:
public MainPage()
{
this.InitializeComponent();
//Properties dictionary filling
prop = new Properties();
prop.Load("URL");
tab = new Bars.TopAppBar();
bab = new Bars.BottomAppBar();
tABar = this.topAppBar;
actvt = this.Activity;
bABar = this.bottomAppBar;
//Constructor of the UserControl
act = new Home(this, prop);
}
The constructor of the UserControl uses the MainPage as a Callback and the class Properties to look for the URL to download the XML file.
What happens is that, as Properties.Load() is an asynchronous method, is called, then the remaining lines are executed and when the program finishes, then goes back to Load() and fills the Dictionary.
As the Home constructor depends on a Value of Properties I get an Exception.
I have tried to create async voids assigning different priorities to force the program to run first one thing and then the remaining, but it hasn't worked either.
So, summarizing, I need to make sure that Properties is filled in the first place, anyone knows how to do it?
Thanks in advance!
Eva if you want to wait until the Load method is finished you have to change this method to return a Task.
public async Task LoadAsync(string uri)...
Better if you put your code in LoadedEventHandler of your page and make this method async. After that you will be able to await Properties.Load method.
If you want to call this method in constructor you can do it like this:
Task.Run(async () =>{
var task = p.LoadAsync().ConfigureAwait(false);
await task;
}).Wait()
But you have to be aware that deadlock can appear if in LoadAsync method will be await without disabled context switching (ConfigureAwait).

Getting a parameterless method to act like a Func<ReturnT>

I'm trying to make a part of my code more fluent.
I have a string extension that makes an HTTP request out of the string and returns the response as a string. So I can do something like...
string _html = "http://www.stackoverflow.com".Request();
I'm trying to write an extension that will keep trying the request until it succeeds. My signature looks something like...
public static T KeepTrying<T>(this Func<T> KeepTryingThis) {
// Code to ignore exceptions and keep trying goes here
// Returns the result of KeepTryingThis if it succeeds
}
I intend to call it something like...
string _html = "http://www.stackoverflow.com".Request.KeepTrying();
Alas, that doesn't seem to work =). I tried making it into a lambda first but that doesn't seem to work either.
string _html = (() => "http://www.stackoverflow.com".Request()).KeepTrying();
Is there a way to do what I'm trying to do while keeping the syntax fairly fluent?
Suggestions much appreciated.
Thanks.
You can't use a method group for extension methods, or lambda expressions. I blogged about this a while ago.
I suspect you could cast to Func<string>:
string _html = ((Func<string>)"http://www.stackoverflow.com".Request)
.KeepTrying();
but that's pretty nasty.
One alternative would be to change Request() to return a Func, and use:
string _html = "http://www.stackoverflow.com".Request().KeepTrying();
Or if you wanted to keep the Request method itself simple, just add a RequestFunc method:
public static Func<string> RequestFunc(this string url)
{
return () => url.Request();
}
and then call:
string _html = "http://www.stackoverflow.com".RequestFunc().KeepTrying();
Why not turn this on its head?
static T KeepTrying<T>(Func<T> func) {
T val = default(T);
while (true) {
try {
val = func();
break;
} catch { }
}
return val;
}
var html = KeepTrying(() => "http://www.stackoverflow.com".Request());
What about enhancing the Request?
string _html = "http://www.stackoverflow.com".Request(RequestOptions.KeepTrying);
string _html = "http://www.stackoverflow.com".Request(RequestOptions.Once);
RequestOptions is a enum. You could also have more options, timeout arguments, number of retries etc.
OR
public static string RepeatingRequest(this string url) {
string response = null;
while ( response != null /* how ever */ ) {
response = url.Request();
}
return response;
}
string _html = "http://www.stackoverflow.com".RepeatingRequest();
AFAIK you can write an extension method that extends a Func<T> delegate, but the compiler doesn't know what do you mean:
string _html = "http://www.stackoverflow.com".Request.KeepTrying(); // won't work
But if you explicitly cast the delegate will work:
string _html = ((Func<string>)"http://www.stackoverflow.com".Request).KeepTrying(); // works
The question here it whether the code readability is really improved in this case by an extension method.
I wouldn't write an extension method for string. Use a more specific type, like the Uri.
The full code:
public static class Extensions
{
public static UriRequest Request(this Uri uri)
{
return new UriRequest(uri);
}
public static UriRequest KeepTrying(this UriRequest uriRequest)
{
uriRequest.KeepTrying = true;
return uriRequest;
}
}
public class UriRequest
{
public Uri Uri { get; set; }
public bool KeepTrying { get; set; }
public UriRequest(Uri uri)
{
this.Uri = uri;
}
public string ToHtml()
{
var client = new System.Net.WebClient();
do
{
try
{
using (var reader = new StreamReader(client.OpenRead(this.Uri)))
{
return reader.ReadToEnd();
}
}
catch (WebException ex)
{
// log ex
}
}
while (KeepTrying);
return null;
}
public static implicit operator string(UriRequest uriRequest)
{
return uriRequest.ToHtml();
}
}
Calling it:
string html = new Uri("http://www.stackoverflow.com").Request().KeepTrying();

Categories