Rewriting class for generic usage - C# MonoTouch - c#

I'm working on an iOS application using Xamarin.iOS / MonoTouch and I've run across a bit of a dilemma. We're downloading quite a bit of data with our application by querying JSON-files which are then processed into models that are saved into our local sqlite-database. The problem is that the class I've written is made for specific types, and I want to be able to use the same class for fetching all JSON data into local objects.
Here is my code:
using System;
using System.IO;
using System.Net;
using Newtonsoft.Json;
using System.Collections.Generic;
#pragma warning disable 0414 // Supressing Warning CS0414:
namespace CommonLibrary {
public class JSONHandler {
// Debug Constants:
private static String DEBUG_FILE_TAG = "[JSONHandler] ";
// Define variables:
private Uri JSONRequestURL;
private bool RequestTimedOut;
private bool RequestSuccessful;
private string ResponseContent;
private List<Post> JSONObjects;
// Define objects:
private HttpWebRequest JSONWebRequest;
private HttpWebResponse JSONWebResponse;
// Constructor:
public JSONHandler(string requestURL){
// Set request URL:
this.JSONRequestURL = new Uri(requestURL);
// Set default statuses:
this.RequestTimedOut = false;
this.RequestSuccessful = false;
}
// Create web request:
private void CreateWebRequest(){
this.JSONWebRequest = (HttpWebRequest) WebRequest.Create (this.JSONRequestURL);
this.JSONWebRequest.Method = "GET";
this.JSONWebRequest.Timeout = 5000;
this.JSONWebRequest.KeepAlive = false;
this.JSONWebRequest.AllowAutoRedirect = false;
this.JSONWebRequest.ContentType = "application/json";
}
// Get request response:
private void GetRequestResponse(){
try {
// Catch the response:
this.JSONWebResponse = (HttpWebResponse) this.JSONWebRequest.GetResponse ();
// Check the status code:
if (this.JSONWebResponse.StatusCode == HttpStatusCode.OK){
// Get content:
StreamReader reader = new StreamReader (this.JSONWebResponse.GetResponseStream ());
this.ResponseContent = reader.ReadToEnd();
// Close response:
this.JSONWebResponse.Close();
// Check response length:
if (!String.IsNullOrWhiteSpace(this.ResponseContent)){
this.JSONObjects = JsonConvert.DeserializeObject<List<Post>>(this.ResponseContent);
this.RequestSuccessful = true;
} else {
this.RequestSuccessful = false;
}
} else {
this.RequestSuccessful = false;
}
} catch (WebException){
this.RequestTimedOut = true;
this.RequestSuccessful = false;
} catch (TimeoutException){
this.RequestTimedOut = true;
this.RequestSuccessful = false;
}
}
// Fetch JSON from server:
public void FetchJSON(){
this.CreateWebRequest ();
this.GetRequestResponse ();
}
// Return request status:
public bool RequestWasSuccessful(){
return RequestSuccessful;
}
// Return timeout status:
public bool RequestDidTimeOut(){
return RequestTimedOut;
}
// Get object count:
public int GetJSONCount(){
return this.JSONObjects.Count;
}
// Get list of objects:
public List<Post> GetJSONObjects (){
return this.JSONObjects;
}
}
}
As you can see, I have to change the type that are stored in the lists from Post to any other object and create a new file, for instance, JSONPost, JSONRunner, JSONLayer etc, and I'd like to handle that with just one class, JSONHandler. Hopefully someone here can help me with this matter. I'm going to have the following classes now:
Post
Layer
RelayTeam
RelayRunner
RelayRunnerResult
MarathonRunner
MarathonRunnerResult
And as you all can understand it won't be good having a duplicated file just for all of these.
I'm really thankful for any help I can get!
Best regards,
Jonathan

Use Generics - if the type of the JSONObjects collection is the only thing that varies, you can do this
public class JSONHandler<T> {
...
private List<T> JSONObjects;
When you create a new JSONHandler instance, you can specify the type
var handler = new JSONHandler<Post>();
var handler = new JSONHandler<Layer>();
var handler = new JSONHandler<RelayTeam>();

Related

Can I do any action just after return my response with an API GraphQL

I am trying to perform an action right after returning my response to a GraphQL API request.
Let me explain: I would like to develop a GraphQL server that receives a certain request from another server and sends a request to this other server just after answering the first request
In comparison, it looks like the OnActionExecuted function with ActionFilterAttribute but I can't implement it for GraphQl, maybe it works only for Rest APIs
I also found the function :
public async override Task AfterExecutionAsync(IExecutionContext context) from class GraphQL.Execution.DocumentExecutionListenerBase but I can't figure out how to implement it so that it triggers after returning my answer
this is the idea but of course it is not what i have tried haha
public async Task<sendLinkRequest> sendLinkStatusUpdate(newLinkStatusInput input)
{
return _sendLinkRequestService.sendLinkStatusUpdate(input);
//Doing this after my function return the result
var graphQLClient = new GraphQLHttpClient("https://exemple.com", new NewtonsoftJsonSerializer());
var sendLinkStatusUpdate = new GraphQLRequest
{
Query = "mutation{sendLinkStatusUpdate(input : {newLinkStatus: \"active\"}) {errors {scope message}}}",
Variables = new
{
newLinkStatus = "active"
}
};
var graphQLResponse = await graphQLClient.SendQueryAsync<sendLinkRequest>(sendLinkStatusUpdate);
}
Anyway, if someone has an idea or a lead I'm interested
Thanks for your comment ! I managed to do it like that :)
public sendLinkRequest sendLinkStatusUpdate(newLinkStatusInput input)
{
TrackingActionFilter ta = new TrackingActionFilter();
ta.ProcessCompleted += ta_nextAction; // register with an event
sendLinkRequest test = ta.StartProcess(input, _sendLinkRequestService);
return test;
}
public async static void ta_nextAction(object sender, EventArgs e)
{
var graphQLClient = new GraphQLHttpClient("https://talentusnewdemo.eu-west-1.beepleapp.eu/b/multi-tenant-api", new NewtonsoftJsonSerializer());
var sendLinkStatusUpdate = new GraphQLRequest
{
Query = "mutation{sendLinkStatusUpdate(input : {newLinkStatus: \"active\"}) {errors {scope message}}}",
Variables = new
{
newLinkStatus = "active"
}
};
var graphQLResponse = await graphQLClient.SendQueryAsync<sendLinkRequest>(sendLinkStatusUpdate);
Console.WriteLine("Process Completed!");
}
public class TrackingActionFilter
{
public event EventHandler ProcessCompleted;
public sendLinkRequest StartProcess(newLinkStatusInput input, IsendLinkRequestService _sendLinkRequestService)
{
Console.WriteLine("Process Started!");
OnProcessCompleted(EventArgs.Empty); //No event data
return _sendLinkRequestService.sendLinkStatusUpdate(input);
}
protected virtual void OnProcessCompleted(EventArgs e)
{
ProcessCompleted?.Invoke(this, e);
}
}

Caching WebAPI 2

EDIT: For each request, a new instance of controller is created. However, this is not true with Attribute classes. Once they are created, it is used for multiple requests. I hope it helps.
I wrote my own WebAPI (using latest version of WebAPI and .net framework) caching action filter. I am aware about CacheCow & this. However, i wanted mine anyways.
However, there is some issue with my code because i don't get exepected output when i use it in my project on live server. On local machine everything works fine.
I used below code in my blog RSS generator and i cache the data for each category. There are around 5 categories (food, tech, personal etc).
Issue: When i navigate to say api/GetTech it returns me the rss feed items from personal blog category. When i navigate to say api/GetPersonal , it returns me api/Food
I am not able to find the root cause but I think this is due to use of static method/variable. I have double checked that my _cachekey has unique value for each category of my blog.
Can someone point out any issues with this code esp when we have say 300 requests per minute ?
public class WebApiOutputCacheAttribute : ActionFilterAttribute
{
// Cache timespan
private readonly int _timespan;
// cache key
private string _cachekey;
// cache repository
private static readonly MemoryCache _webApiCache = MemoryCache.Default;
/// <summary>
/// Initializes a new instance of the <see cref="WebApiOutputCacheAttribute"/> class.
/// </summary>
/// <param name="timespan">The timespan in seconds.</param>
public WebApiOutputCacheAttribute(int timespan)
{
_timespan = timespan;
}
public override void OnActionExecuting(HttpActionContext ac)
{
if (ac != null)
{
_cachekey = ac.Request.RequestUri.PathAndQuery.ToUpperInvariant();
if (!_webApiCache.Contains(_cachekey)) return;
var val = (string)_webApiCache.Get(_cachekey);
if (val == null) return;
ac.Response = ac.Request.CreateResponse();
ac.Response.Content = new StringContent(val);
var contenttype = (MediaTypeHeaderValue)_webApiCache.Get("response-ct") ?? new MediaTypeHeaderValue("application/rss+xml");
ac.Response.Content.Headers.ContentType = contenttype;
}
else
{
throw new ArgumentNullException("ac");
}
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (_webApiCache.Contains(_cachekey)) return;
var body = actionExecutedContext.Response.Content.ReadAsStringAsync().Result;
if (actionExecutedContext.Response.StatusCode == HttpStatusCode.OK)
{
lock (WebApiCache)
{
_wbApiCache.Add(_cachekey, body, DateTime.Now.AddSeconds(_timespan));
_webApiCache.Add("response-ct", actionExecutedContext.Response.Content.Headers.ContentType, DateTimeOffset.UtcNow.AddSeconds(_timespan));
}
}
}
}
The same WebApiOutputCacheAttribute instance can be used to cache multiple simultaneous requests, so you should not store cache keys on the instance of the attribute. Instead, regenerate the cache key during each request / method override. The following attribute works to cache HTTP GET requests.
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using Newtonsoft.Json;
// based on strathweb implementation
// http://www.strathweb.com/2012/05/output-caching-in-asp-net-web-api/
public class CacheHttpGetAttribute : ActionFilterAttribute
{
public int Duration { get; set; }
public ILogExceptions ExceptionLogger { get; set; }
public IProvideCache CacheProvider { get; set; }
private bool IsCacheable(HttpRequestMessage request)
{
if (Duration < 1)
throw new InvalidOperationException("Duration must be greater than zero.");
// only cache for GET requests
return request.Method == HttpMethod.Get;
}
private CacheControlHeaderValue SetClientCache()
{
var cachecontrol = new CacheControlHeaderValue
{
MaxAge = TimeSpan.FromSeconds(Duration),
MustRevalidate = true,
};
return cachecontrol;
}
private static string GetServerCacheKey(HttpRequestMessage request)
{
var acceptHeaders = request.Headers.Accept;
var acceptHeader = acceptHeaders.Any() ? acceptHeaders.First().ToString() : "*/*";
return string.Join(":", new[]
{
request.RequestUri.AbsoluteUri,
acceptHeader,
});
}
private static string GetClientCacheKey(string serverCacheKey)
{
return string.Join(":", new[]
{
serverCacheKey,
"response-content-type",
});
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext == null) throw new ArgumentNullException("actionContext");
var request = actionContext.Request;
if (!IsCacheable(request)) return;
try
{
// do NOT store cache keys on this attribute because the same instance
// can be reused for multiple requests
var serverCacheKey = GetServerCacheKey(request);
var clientCacheKey = GetClientCacheKey(serverCacheKey);
if (CacheProvider.Contains(serverCacheKey))
{
var serverValue = CacheProvider.Get(serverCacheKey);
var clientValue = CacheProvider.Get(clientCacheKey);
if (serverValue == null) return;
var contentType = clientValue != null
? JsonConvert.DeserializeObject<MediaTypeHeaderValue>(clientValue.ToString())
: new MediaTypeHeaderValue(serverCacheKey.Substring(serverCacheKey.LastIndexOf(':') + 1));
actionContext.Response = actionContext.Request.CreateResponse();
// do not try to create a string content if the value is binary
actionContext.Response.Content = serverValue is byte[]
? new ByteArrayContent((byte[])serverValue)
: new StringContent(serverValue.ToString());
actionContext.Response.Content.Headers.ContentType = contentType;
actionContext.Response.Headers.CacheControl = SetClientCache();
}
}
catch (Exception ex)
{
ExceptionLogger.Log(ex);
}
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
try
{
var request = actionExecutedContext.Request;
// do NOT store cache keys on this attribute because the same instance
// can be reused for multiple requests
var serverCacheKey = GetServerCacheKey(request);
var clientCacheKey = GetClientCacheKey(serverCacheKey);
if (!CacheProvider.Contains(serverCacheKey))
{
var contentType = actionExecutedContext.Response.Content.Headers.ContentType;
object serverValue;
if (contentType.MediaType.StartsWith("image/"))
serverValue = actionExecutedContext.Response.Content.ReadAsByteArrayAsync().Result;
else
serverValue = actionExecutedContext.Response.Content.ReadAsStringAsync().Result;
var clientValue = JsonConvert.SerializeObject(
new
{
contentType.MediaType,
contentType.CharSet,
});
CacheProvider.Add(serverCacheKey, serverValue, new TimeSpan(0, 0, Duration));
CacheProvider.Add(clientCacheKey, clientValue, new TimeSpan(0, 0, Duration));
}
if (IsCacheable(actionExecutedContext.Request))
actionExecutedContext.ActionContext.Response.Headers.CacheControl = SetClientCache();
}
catch (Exception ex)
{
ExceptionLogger.Log(ex);
}
}
}
Just replace the CacheProvider with your MemoryCache.Default. In fact, the code above uses the same by default during development, and uses azure cache when deployed to a live server.
Even though your code resets the _cachekey instance field during each request, these attributes are not like controllers where a new one is created for each request. Instead, the attribute instance can be repurposed to service multiple simultaneous requests. So don't use an instance field to store it, regenerate it based on the request each and every time you need it.

Avoid fxcop error CA1004 in this case

I have a case where it is returning objects of type T. My code looks like this.
public static T GetObjectsFromWebRequest<T>(string urlPath) where T : class
{
T modelObjects;
try
{
//SaveServiceDataIntoTextFile(urlPath);
WebRequest request = WebRequest.Create(urlPath);
WebResponse ws = request.GetResponse();
StreamReader responseStream = new StreamReader(ws.GetResponseStream());
//Get the response of the webrequest into a string
string response = responseStream.ReadToEnd();
modelObjects = XMLSerializeDeserialize.ConvertXMLToModel<T>(response);
}
catch (Exception)
{
throw;
}
return modelObjects;
}
In this case I don't have any option but add a default parameter like
public static T GetObjectsFromWebRequest<T>(string urlPath, T a = null) where T : class
Is there any other way I can resolve this violation?
As suggested here, you could use an out parameter to convey your result:
public static void GetObjectsFromWebRequest<T>(string urlPath, out T objects) ...

Use WebClient with socks proxy

Is there any way to use a socks proxy with WebClient? Specifically with the DownloadString method that it provides?
I don't want to use any third party stuff like privoxy, freecap whatever and I can't use commercial libraries like those from Chilkat. I tried using stuff from http://www.mentalis.org/ in fact I used their WebRequest implementation but they don't seem to have something similar for WebClient.
SOCKS is not supported directly by the WebRequest/WebResponse classes and by extension, the WebClient class (it relies on WebRequest to do its work).
It really can't, as it works on the transport layer (TCP/IP) and not through a simple redirect to a server that forwards HTTP requests (which is the level that the WebRequest/WebResponse classes work on).
You can create a specialized derivation of WebRequest/WebResponse (that uses ProxySocket to handle the low-level handshaking and then) and then create a specialized WebClient class which overrides the GetWebRequest and GetWebResponse methods.
Once you have that class substituted for your WebClient instances, it should work as normal (you might have to set up the proxy in each case where you use it though).
Here is how I ended up doing it, thank you casperOne for the answer
public class SocksWebClient : WebClient
{
public IProxyDetails ProxyDetails { get; set; }
public string UserAgent { get; set; }
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest result = null;
if (ProxyDetails != null)
{
if (ProxyDetails.ProxyType == ProxyType.Proxy)
{
result = (HttpWebRequest)WebRequest.Create(address);
result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress);
if (!string.IsNullOrEmpty(UserAgent))
((HttpWebRequest)result).UserAgent = UserAgent;
}
else if (ProxyDetails.ProxyType == ProxyType.Socks)
{
result = SocksHttpWebRequest.Create(address);
result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress);
//TODO: implement user and password
}
else if (ProxyDetails.ProxyType == ProxyType.None)
{
result = (HttpWebRequest)WebRequest.Create(address);
if (!string.IsNullOrEmpty(UserAgent))
((HttpWebRequest)result).UserAgent = UserAgent;
}
}
else
{
result = (HttpWebRequest)WebRequest.Create(address);
if (!string.IsNullOrEmpty(UserAgent))
((HttpWebRequest)result).UserAgent = UserAgent;
}
return result;
}
}
The SocksHttpWebRequest class is taken from the blog linked to by #casperOne, the code for which is as follows:
using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Org.Mentalis.Network.ProxySocket;
namespace Ditrans
{
public class SocksHttpWebRequest : WebRequest
{
#region Member Variables
private readonly Uri _requestUri;
private WebHeaderCollection _requestHeaders;
private string _method;
private SocksHttpWebResponse _response;
private string _requestMessage;
private byte[] _requestContentBuffer;
// darn MS for making everything internal (yeah, I'm talking about you, System.net.KnownHttpVerb)
static readonly StringCollection validHttpVerbs =
new StringCollection { "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "OPTIONS" };
#endregion
#region Constructor
private SocksHttpWebRequest(Uri requestUri)
{
_requestUri = requestUri;
}
#endregion
#region WebRequest Members
public override WebResponse GetResponse()
{
if (Proxy == null)
{
throw new InvalidOperationException("Proxy property cannot be null.");
}
if (String.IsNullOrEmpty(Method))
{
throw new InvalidOperationException("Method has not been set.");
}
if (RequestSubmitted)
{
return _response;
}
_response = InternalGetResponse();
RequestSubmitted = true;
return _response;
}
public override Uri RequestUri
{
get { return _requestUri; }
}
public override IWebProxy Proxy { get; set; }
public override WebHeaderCollection Headers
{
get
{
if (_requestHeaders == null)
{
_requestHeaders = new WebHeaderCollection();
}
return _requestHeaders;
}
set
{
if (RequestSubmitted)
{
throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
}
_requestHeaders = value;
}
}
public bool RequestSubmitted { get; private set; }
public override string Method
{
get
{
return _method ?? "GET";
}
set
{
if (validHttpVerbs.Contains(value))
{
_method = value;
}
else
{
throw new ArgumentOutOfRangeException("value", string.Format("'{0}' is not a known HTTP verb.", value));
}
}
}
public override long ContentLength { get; set; }
public override string ContentType { get; set; }
public override Stream GetRequestStream()
{
if (RequestSubmitted)
{
throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
}
if (_requestContentBuffer == null)
{
_requestContentBuffer = new byte[ContentLength];
}
else if (ContentLength == default(long))
{
_requestContentBuffer = new byte[int.MaxValue];
}
else if (_requestContentBuffer.Length != ContentLength)
{
Array.Resize(ref _requestContentBuffer, (int) ContentLength);
}
return new MemoryStream(_requestContentBuffer);
}
#endregion
#region Methods
public static new WebRequest Create(string requestUri)
{
return new SocksHttpWebRequest(new Uri(requestUri));
}
public static new WebRequest Create(Uri requestUri)
{
return new SocksHttpWebRequest(requestUri);
}
private string BuildHttpRequestMessage()
{
if (RequestSubmitted)
{
throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
}
var message = new StringBuilder();
message.AppendFormat("{0} {1} HTTP/1.0\r\nHost: {2}\r\n", Method, RequestUri.PathAndQuery, RequestUri.Host);
// add the headers
foreach (var key in Headers.Keys)
{
message.AppendFormat("{0}: {1}\r\n", key, Headers[key.ToString()]);
}
if (!string.IsNullOrEmpty(ContentType))
{
message.AppendFormat("Content-Type: {0}\r\n", ContentType);
}
if (ContentLength > 0)
{
message.AppendFormat("Content-Length: {0}\r\n", ContentLength);
}
// add a blank line to indicate the end of the headers
message.Append("\r\n");
// add content
if(_requestContentBuffer != null && _requestContentBuffer.Length > 0)
{
using (var stream = new MemoryStream(_requestContentBuffer, false))
{
using (var reader = new StreamReader(stream))
{
message.Append(reader.ReadToEnd());
}
}
}
return message.ToString();
}
private SocksHttpWebResponse InternalGetResponse()
{
var response = new StringBuilder();
using (var _socksConnection =
new ProxySocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
var proxyUri = Proxy.GetProxy(RequestUri);
var ipAddress = GetProxyIpAddress(proxyUri);
_socksConnection.ProxyEndPoint = new IPEndPoint(ipAddress, proxyUri.Port);
_socksConnection.ProxyType = ProxyTypes.Socks5;
// open connection
_socksConnection.Connect(RequestUri.Host, 80);
// send an HTTP request
_socksConnection.Send(Encoding.ASCII.GetBytes(RequestMessage));
// read the HTTP reply
var buffer = new byte[1024];
var bytesReceived = _socksConnection.Receive(buffer);
while (bytesReceived > 0)
{
response.Append(Encoding.ASCII.GetString(buffer, 0, bytesReceived));
bytesReceived = _socksConnection.Receive(buffer);
}
}
return new SocksHttpWebResponse(response.ToString());
}
private static IPAddress GetProxyIpAddress(Uri proxyUri)
{
IPAddress ipAddress;
if (!IPAddress.TryParse(proxyUri.Host, out ipAddress))
{
try
{
return Dns.GetHostEntry(proxyUri.Host).AddressList[0];
}
catch (Exception e)
{
throw new InvalidOperationException(
string.Format("Unable to resolve proxy hostname '{0}' to a valid IP address.", proxyUri.Host), e);
}
}
return ipAddress;
}
#endregion
#region Properties
public string RequestMessage
{
get
{
if (string.IsNullOrEmpty(_requestMessage))
{
_requestMessage = BuildHttpRequestMessage();
}
return _requestMessage;
}
}
#endregion
}
}
Note that, as #casperOne pointed out, this makes use of a (free) third party library called ProxySocket.
I came across this aswell and found the nice BetterHttpClient
It derives from WebClient and allows you to specify a socks proxy:
BetterHttpClient.HttpClient client = new BetterHttpClient.HttpClient(new BetterHttpClient.Proxy("IP address", port, BetterHttpClient.ProxyTypeEnum.Socks));
I was looking for library in order to do this. finally i found the MihaZupan/HttpToSocks5Proxy it's a real lifesaver. just like this you can use it:
using MihaZupan;
var proxy = new HttpToSocks5Proxy("127.0.0.1", 1080,
"username", "password" // optional
);
var handler = new HttpClientHandler { Proxy = proxy };
HttpClient httpClient = new HttpClient(handler, true);
var result = await httpClient.SendAsync(
new HttpRequestMessage(HttpMethod.Get, "https://httpbin.org/ip"));
Console.WriteLine("HTTPS GET: " + await result.Content.ReadAsStringAsync());
If you want a more convenient, WebClient-esque API for reading the content, you can wrap the response-read calls into an extension method:
public static class ExtensionMethods
{
public static async Task<string> DownloadStringAsync(this HttpClient client, string url)
{
var response = await client.SendAsync(new HttpRequestMessage(
HttpMethod.Get, url));
return await response.Content.ReadAsStringAsync();
}
}
For future readers:
Since .NET 6, socks proxies are supported natively with HttpClient.
var handler = new HttpClientHandler
{
Proxy = new WebProxy("socks5://127.0.0.1", 9050)
};
var httpClient = new HttpClient(handler);
Note:
In time of writing this answer, WebClient is considered as obsolete. HttpClient should be used instead.
Try Yove.Proxy. Example:
using (var w = new WebClient())
{
using (var proxy = new ProxyClient("67.201.33.10", 25283, ProxyType.Socks5))
{
w.Proxy = proxy;
Console.WriteLine(w.DownloadString("https://api.ipify.org"));
}
}

Use Script# to compile code (stand-alone)

I'm trying to compile small fragments of C# into JavaScript using the Script# compiler.
But I don't get anything in return, GetStream() in my MemoryStreamSource is not even being called, so I must be doing something wrong.
Here's my code:
CodeScriptCompiler csc = new CodeScriptCompiler();
return csc.CompileCSharp("String.IsNullOrWhiteSpace(Model.MobilePhoneNumber)");
CodeScriptCompiler.cs
using System;
using System.Collections.Generic;
using ScriptSharp;
namespace CodeToScriptCompiler
{
public class CodeScriptCompiler
{
ScriptCompiler sc = new ScriptCompiler();
public string CompileCSharp(string csharpCode)
{
string errorMessages = String.Empty;
CompilerOptions options = new CompilerOptions();
options.Defines = new List<string>();
options.References = new List<string>();
options.References.Add("System.dll");
options.Resources = new List<IStreamSource>();
options.Sources = new List<IStreamSource>();
options.Sources.Add(new MemoryStreamSource(csharpCode));
options.TemplateFile = new MemoryStreamSource(csharpCode);
MemoryStreamDestination output = new MemoryStreamDestination();
options.ScriptFile = output;
if (!options.Validate(out errorMessages))
{
return errorMessages;
}
return output.GetCompiledCode();
}
}
}
MemoryStreamSource.cs
using System.IO;
using System.Text;
using ScriptSharp;
namespace CodeToScriptCompiler
{
public class MemoryStreamSource : IStreamSource
{
private string _code;
private MemoryStream _memoryStream;
public MemoryStreamSource(string code)
{
this._code = code;
}
public string Name
{
get { return "InMemoryCode"; }
}
public string FullName
{
get { return "InMemoryCode"; }
}
public void CloseStream(Stream stream)
{
stream.Close();
}
public Stream GetStream()
{
this._memoryStream = new MemoryStream(Encoding.ASCII.GetBytes(this._code));
return this._memoryStream;
}
}
}
MemoryStreamDestination.cs
using System;
using System.IO;
using ScriptSharp;
namespace CodeToScriptCompiler
{
public class MemoryStreamDestination : IStreamSource
{
private MemoryStream _memoryStream;
private string _compiledCode;
public string Name
{
get { return "MemoryStreamDestination"; }
}
public string FullName
{
get { return "MemoryStreamDestination"; }
}
public void CloseStream(Stream stream)
{
if (String.IsNullOrWhiteSpace(this._compiledCode))
{
this._compiledCode = this.GetCompiledCode();
}
stream.Close();
}
public Stream GetStream()
{
this._memoryStream = new MemoryStream();
return this._memoryStream;
}
public string GetCompiledCode()
{
if (!String.IsNullOrWhiteSpace(this._compiledCode))
{
return this._compiledCode;
}
if (this._memoryStream != null)
{
using (StreamReader sr = new StreamReader(this._memoryStream))
{
return sr.ReadToEnd();
}
}
return String.Empty;
}
}
}
Some things I see potentially problematic.
TemplateFile is set to a c# code stream. Leave it unset, since that is not a valid template.
References should include the script# mscorlib, and furthermore, only full paths to valid script# assemblies. System.dll is not a script# assembly.
Before you read from the MemoryStream, you need to set the stream position back to the start, otherwise it is at the end after the compiler has written to it, and there is nothing more to read.
Not seeing a call to Compile on the Compiler instance you created, passing in the options instance. My guess is you did do that, just not there in the stack overflow snippet.
You probably should also implement IErrorHandler and pass that to the compiler to get error messages should they occur, once you have the basic thing working.
For reference you can also look at the unit tests at https://github.com/nikhilk/scriptsharp/tree/master/tests/ScriptSharp/Core which does something similar.
Note that you'll need a valid c# source file, rather than a single standalone expression. You can however likely deal with that by stripping off stuff from the start and end of the resulting script to get the script for just the expression you care about.
Hope that helps.
I am certainly interested/curious to understand how you're using this, and where you're compiling c# to script dynamically...

Categories

Resources