I decided to help my friend with a project he's working on. I'm trying to write a test webpage for him to verify some new functionality, but in my auto-generated code I get
CS1106: Extension method must be defined in a non-generic static class
Implementing the code in index.cshtml isn't the best way to do this, but we are just trying to do a proof of concept and will do a proper implementation later.
In all the places I looked they pretty much said that all the functions I define must be in a static class (as the error states). That wouldn't be so bad except for the class that holds all my functions is auto-generated and not static. I'm not really sure what settings I can change to fix this.
Here is a copy of the relevant (I believe) parts of code. The implementation of some or all of the functions may be incorrect. I haven't tested them yet
#{
HttpRequest req = System.Web.HttpContext.Current.Request;
HttpResponse resp = System.Web.HttpContext.Current.Response;
var url = req.QueryString["url"];
//1 Download web data from URL
//2 Write the final edited version of the document to the response object using resp.write(String x);
//3 Add Script tag for dom-outline-1.0 to html agility pack document
//4 Search for relative URLs and correct them to become absolute URL's that point back to the hostname
}
#functions
{
public static void PrintNodes(this HtmlAgilityPack.HtmlNode tag)
{
HttpResponse resp = System.Web.HttpContext.Current.Response;
resp.Write(tag.Name + tag.InnerHtml);
if (!tag.HasChildNodes)
{
return;
}
PrintNodes(tag.FirstChild);
}
public static void AddScriptNode(this HtmlAgilityPack.HtmlNode headNode, HtmlAgilityPack.HtmlDocument htmlDoc, string filePath)
{
string content = "";
using (StreamReader rdr = File.OpenText(filePath))
{
content = rdr.ReadToEnd();
}
if (headNode != null)
{
HtmlAgilityPack.HtmlNode scripts = htmlDoc.CreateElement("script");
scripts.Attributes.Add("type", "text/javascript");
scripts.AppendChild(htmlDoc.CreateComment("\n" + content + "\n"));
headNode.AppendChild(scripts);
}
}
}
<HTML CODE HERE>
If you were really smart you would encapsulate the design to take Delegates, reason being if you use a delegate you don't have to worry about referencing something static.
public delegate void MyUrlThing(string url, object optional = null);
Possibly some state...
public enum UrlState
{
None,
Good,
Bad
}
Then void would become UrlState...
Also if you wanted you could also setup a text box and blindly give it CIL....
Then you would compile the delegates using something like this
http://www.codeproject.com/Articles/578116/Complete-Managed-Media-Aggregation-Part-III-Quantu
This way you can use also then optionally just use the IL to augment whatever you wanted.
You could also give it CSharp code I suppose...
If you want to keep you design you can also then optionally use interfaces... and then put the compiled dll in a directory and then load it etc... as traditionally
Related
I'm investigating the use of the Roslyn compiler within a Visual Studio Extension (VSIX) that uses the VisualStudioWorkspace to update existing code. Having spent the last few days reading up on this, there seem to be several ways to achieve this....I'm just not sure which is the best approach for me.
Okay, so let's assume that the User has their solution open in Visual Studio 2015. They click on my Extension and (via a form) they tell me that they want to add the following method definition to an interface:
GetSomeDataResponse GetSomeData(GetSomeDataRequest request);
They also tell me the name of the interface, it's ITheInterface.
The interface already has some code in it:
namespace TheProjectName.Interfaces
{
using System;
public interface ITheInterface
{
/// <summary>
/// A lonely method.
/// </summary>
LonelyMethodResponse LonelyMethod(LonelyMethodRequest request);
}
}
Okay, so I can load the Interface Document using the following:
Document myInterface = this.Workspace.CurrentSolution?.Projects?
.FirstOrDefault(p
=> p.Name.Equals("TheProjectName"))
?.Documents?
.FirstOrDefault(d
=> d.Name.Equals("ITheInterface.cs"));
So, what is the best way to now add my new method to this existing interface, ideally writing in the XML comment (triple-slash comment) too? Bear in mind that the request and response types (GetSomeDataRequest and GetSomeDataResponse) may not actually exist yet. I'm very new to this, so if you can provide code examples then that would be terrific.
UPDATE
I decided that (probably) the best approach would be simply to inject in some text, rather than try to programmatically build up the method declaration.
I tried the following, but ended up with an exception that I don't comprehend:
SourceText sourceText = await myInterface.GetTextAsync();
string text = sourceText.ToString();
var sb = new StringBuilder();
// I want to all the text up to and including the last
// method, but without the closing "}" for the interface and the namespace
sb.Append(text.Substring(0, text.LastIndexOf("}", text.LastIndexOf("}") - 1)));
// Now add my method and close the interface and namespace.
sb.AppendLine("GetSomeDataResponse GetSomeData(GetSomeDataRequest request);");
sb.AppendLine("}");
sb.AppendLine("}");
Inspecting this, it's all good (my real code adds formatting and XML comments, but removed that for clarity).
So, knowing that these are immutable, I tried to save it as follows:
var updatedSourceText = SourceText.From(sb.ToString());
var newInterfaceDocument = myInterface.WithText(updatedSourceText);
var newProject = newInterfaceDocument.Project;
var newSolution = newProject.Solution;
this.Workspace.TryApplyChanges(newSolution);
But this created the following exception:
bufferAdapter is not a VsTextDocData
at Microsoft.VisualStudio.Editor.Implementation.VsEditorAdaptersFactoryService.GetAdapter(IVsTextBuffer bufferAdapter)
at Microsoft.VisualStudio.Editor.Implementation.VsEditorAdaptersFactoryService.GetDocumentBuffer(IVsTextBuffer bufferAdapter)
at Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.InvisibleEditor..ctor(IServiceProvider serviceProvider, String filePath, Boolean needsSave, Boolean needsUndoDisabled)
at Microsoft.VisualStudio.LanguageServices.RoslynVisualStudioWorkspace.OpenInvisibleEditor(IVisualStudioHostDocument hostDocument)
at Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.DocumentProvider.StandardTextDocument.UpdateText(SourceText newText)
at Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.VisualStudioWorkspaceImpl.ApplyDocumentTextChanged(DocumentId documentId, SourceText newText)
at Microsoft.CodeAnalysis.Workspace.ApplyProjectChanges(ProjectChanges projectChanges)
at Microsoft.CodeAnalysis.Workspace.TryApplyChanges(Solution newSolution)
at Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.VisualStudioWorkspaceImpl.TryApplyChanges(Solution newSolution)
If I were you I would take advantage of all Roslyn benefits, i.e. I would work with the SyntaxTree of the Document rather than processing the files text (you are able to do the latter without using Roslyn at all).
For instance:
...
SyntaxNode root = await document.GetSyntaxRootAsync().ConfigureAwait(false);
var interfaceDeclaration = root.DescendantNodes(node => node.IsKind(SyntaxKind.InterfaceDeclaration)).FirstOrDefault() as InterfaceDeclarationSyntax;
if (interfaceDeclaration == null) return;
var methodToInsert= GetMethodDeclarationSyntax(returnTypeName: "GetSomeDataResponse ",
methodName: "GetSomeData",
parameterTypes: new[] { "GetSomeDataRequest" },
paramterNames: new[] { "request" });
var newInterfaceDeclaration = interfaceDeclaration.AddMembers(methodToInsert);
var newRoot = root.ReplaceNode(interfaceDeclaration, newInterfaceDeclaration);
// this will format all nodes that have Formatter.Annotation
newRoot = Formatter.Format(newRoot, Formatter.Annotation, workspace);
workspace.TryApplyChanges(document.WithSyntaxRoot(newRoot).Project.Solution);
...
public MethodDeclarationSyntax GetMethodDeclarationSyntax(string returnTypeName, string methodName, string[] parameterTypes, string[] paramterNames)
{
var parameterList = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(GetParametersList(parameterTypes, paramterNames)));
return SyntaxFactory.MethodDeclaration(attributeLists: SyntaxFactory.List<AttributeListSyntax>(),
modifiers: SyntaxFactory.TokenList(),
returnType: SyntaxFactory.ParseTypeName(returnTypeName),
explicitInterfaceSpecifier: null,
identifier: SyntaxFactory.Identifier(methodName),
typeParameterList: null,
parameterList: parameterList,
constraintClauses: SyntaxFactory.List<TypeParameterConstraintClauseSyntax>(),
body: null,
semicolonToken: SyntaxFactory.Token(SyntaxKind.SemicolonToken))
// Annotate that this node should be formatted
.WithAdditionalAnnotations(Formatter.Annotation);
}
private IEnumerable<ParameterSyntax> GetParametersList(string[] parameterTypes, string[] paramterNames)
{
for (int i = 0; i < parameterTypes.Length; i++)
{
yield return SyntaxFactory.Parameter(attributeLists: SyntaxFactory.List<AttributeListSyntax>(),
modifiers: SyntaxFactory.TokenList(),
type: SyntaxFactory.ParseTypeName(parameterTypes[i]),
identifier: SyntaxFactory.Identifier(paramterNames[i]),
#default: null);
}
}
Note that this is pretty raw code, Roslyn API is extremely powerful when it comes to analyzing/processing the syntax tree, getting symbol information/references and so on. I would recommend you to look at this page and this page for reference.
Please bear with me as I am new to Silverlight. I need to write a web api wrapper (I've named it WebClientWrapper) which is supposed to consume rest services. The project uses Silverlight 5. I am facing many problems while writing such a wrapper. There are a lot of examples demonstrating rest service consumption in C#. But unfortunately none of them worked for me. It's been a challenge for me to get done with what I need. Below are my requirements:
1) UI should not freeze while I make any GET request,
2) Calling methods from WebClientWrapper should be as easier as possible.
3) .NET framework version of project should not be changed.
I tried following things so far:
1) Use of HttpClient. I referred this link: http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from-a-net-client. The problem with this approach is I cannot call ReadAsAsync method. Calling this method requires change in framework (i.e. replacing dlls or changing framework version) which is not feasible.
2) Use of WebClient. http://www.kastory.net/index.php?option=com_content&view=article&id=25:rest-web-service-in-c-with-silverlight-and-asp-net-web-api&catid=32:web-services&Itemid=130.
The problem with this is I will probably (not sure!) have to make changes in the way I call methods from WebClientWrapper. I am trying to accomplish calling method from WebClientWrapper something like this:
var appointments = WebClientWrapper.Get<Appointments>(new Dictionary<string, string>()
{
{"hospitalId", "19465654546"}
}, "appointment");
Below is the code snippet I tried in WebClientWrapper.
private const string BaseUrl = "http://localhost:63455/api";
private static WebClient GetClient()
{
int leadingInt = new Random().Next(10000, 99999);
int trailingInt = new Random().Next(1000, 9999);
string date = DateTime.Now.ToString("ddHHmmMMssMMyyyyyss");
string ticketString = string.Format("{0}{1}{2}", leadingInt, date, trailingInt);
var client = new WebClient();
client.Headers["Accept"] = ticketString;
client.Headers["UserAgent"] = "ReceptionistApp";
return client;
}
private static void DownloadCompletionHandler<T>(object sender, DownloadStringCompletedEventArgs e)
{
Encoding messageEncoding = Encoding.UTF8;
var serializer = new DataContractJsonSerializer(typeof (T));
var memoryStream = new MemoryStream(messageEncoding.GetBytes(e.Result));
var objectToReturn = (T) serializer.ReadObject(memoryStream);
}
public static T Get<T>(Dictionary<string, string> paramDictionary, string controller)
{
string absoluteUrl = BaseUrl + controller + "?";
absoluteUrl = paramDictionary.Aggregate(absoluteUrl,
(current, keyValuePair) => current + (keyValuePair.Key + "=" + keyValuePair.Value + "&"));
absoluteUrl = absoluteUrl.TrimEnd('&');
WebClient client = GetClient();
client.DownloadStringCompleted += DownloadCompletionHandler<T>;
client.DownloadStringAsync(new Uri(absoluteUrl));
}
Below are the things I want to mention regarding the code above:
1) As obvious, compiler throws error for the method Get<T> since I've not returned object of type T. How shall I get that object from DownloadStringAsync? I know I can use DownloadStringTaskAsync. But it is not available with the current framework. What changes do I have to make in my code so that I get appointments as shown in the Get<Appointments> method call?
2) DownloadCompletionHandler<T> is bound to return void but actually I want to return objectToReturn as shown in code.
Help will be greatly appreciated. Any new code snippets that will fulfill my requirements are welcome.
RestSharp helped me instead of WebClient.
I am currently developing an Excel macro which allows creating Bugs in a Bugzilla instance.
After some trial and error this now turns out to work fine.
I wanted to enhance the client so that it's also possible to add screenshots to the newly created bug.
The environment I'm using is a little bit tricky:
I have to use MS Excel for my task.
As Excel does not understand XML-RPC, I downloaded an interface DLL (CookComputing.XmlRpcV2.dll from xml-rpc.net) which makes the XML-RPC interface accessible from .NET.
Then I created an additional DLL which can be called from Excel macros (using COM interop).
As already mentioned, this is working fine for tasks like browsing or adding new bugs.
But when adding an attachment to the bug, the image must be converted into a base64 data type. Although this seems to work fine and although the creation of the screenshot seems to succeed, the image seems to be corrupted and cannot be displayed.
Here's what I do to add the image:
The Bugzilla add_attachment method accepts a struct as input:
http://www.bugzilla.org/docs/4.0/en/html/api/Bugzilla/WebService/Bug.html#add_attachment.
This type was defined in C# and is visible also in VBA.
This is the struct definition:
[ClassInterface(ClassInterfaceType.AutoDual)]
public class TAttachmentInputData
{
public string[] ids;
public string data; // base64-encoded data
public string file_name;
public string summary;
public string content_type;
public string comment;
public bool is_patch;
public bool is_private;
public void addId(int id)
{
ids = new string[1];
ids[0] = id.ToString();
}
public void addData(string strData)
{
try
{
byte[] encData_byte = new byte[strData.Length];
encData_byte = System.Text.Encoding.ASCII.GetBytes(strData);
string encodedData = Convert.ToBase64String(encData_byte);
data = new Byte[System.Text.Encoding.ASCII.GetBytes(encodedData).Length];
data = System.Text.Encoding.ASCII.GetBytes(encodedData);
}
catch (Exception e)
{
throw new Exception("Error in base64Encode" + e.Message);
}
}
This is the part in my macro where I would like to add the attachment:
Dim attachmentsStruct As New TAttachmentInputData
fname = attachmentFileName
attachmentsStruct.file_name = GetFilenameFromPath(fname)
attachmentsStruct.is_patch = False
attachmentsStruct.is_private = False
'multiple other definitions
Open fname For Binary As #1
attachmentsStruct.addData (Input(LOF(1), #1))
Close #1
attachmentsStruct.file_name = GetFilenameFromPath(fname)
Call BugzillaClass.add_attachment(attachmentsStruct)
Where BugzillaClass it the interface exposed from my DLL to Excel VBA.
The method add_attachment refers to the XML-RPC method add_attachment.
I assume that my problem is the conversion from the binary file into base64.
This is done using the addData method in my C# DLL.
Is the conversion done correctly there?
Any idea why the images are corrupted?
I think the issue is that you are reading in binary data in the macro, but the addData method is expecting a string. Try declaring the parameter in addData as byte[].
I need to create a html parser, that given a blog url, it returns a list, with all the posts in the page.
I.e. if a page has 10 posts, it
should return a list of 10 divs,
where each div contains h1 and
a p
I can't use its rss feed, because I need to know exactly how it looks like for the user, if it has any ad, image etc and in contrast some blogs have just a summary of its content and the feed has it all, and vice-versa.
Anyway, I've made one that download its feed, and search the html for similar content, it works very well for some blogs, but not for others.
I don't think I can make a parser that works for 100% of the blogs it parses, but I want to make the best possible.
What should be the best approach? Look for tags that have its id attribute equal "post", "content"? Look for p tags? etc etc etc...
Thanks in advance for any help!
I don't think you will be successful on that. You might be able to parse one blog, but if the blog engine changes stuff, it won't work any more. I also don't think you'll be able to write a generic parser. You might even be partially successful, but it's going to be an ethereal success, because everything is so error prone on this context. If you need content, you should go with RSS. If you need to store (simply store) how it looks, you can also do that. But parsing by the way it looks? I don't see concrete success on that.
"Best possible" turns out to be "best reasonable," and you get to define what is reasonable. You can get a very large number of blogs by looking at how common blogging tools (WordPress, LiveJournal, etc.) generate their pages, and code specially for each one.
The general case turns out to be a very hard problem because every blogging tool has its own format. You might be able to infer things using "standard" identifiers like "post", "content", etc., but it's doubtful.
You'll also have difficulty with ads. A lot of ads are generated with JavaScript. So downloading the page will give you just the JavaScript code rather than the HTML that gets generated. If you really want to identify the ads, you'll have to identify the JavaScript code that generates them. Or, your program will have to execute the JavaScript to create the final DOM. And then you're faced with a problem similar to that above: figuring out if some particular bit of HTML is an ad.
There are heuristic methods that are somewhat successful. Check out Identifying a Page's Primary Content for answers to a similar question.
Use the HTML Agility pack. It is an HTML parser made for this.
I just did something like this for our company's blog which uses wordpress. This is good for us because our wordress blog hasn't changed in years, but the others are right in that if your html changes a lot, parsing becomes a cumbersome solution.
Here is what I recommend:
Using Nuget install RestSharp and HtmlAgilityPack. Then download fizzler and include those references in your project (http://code.google.com/p/fizzler/downloads/list).
Here is some sample code I used to implement the blog's search on my site.
using System;
using System.Collections.Generic;
using Fizzler.Systems.HtmlAgilityPack;
using RestSharp;
using RestSharp.Contrib;
namespace BlogSearch
{
public class BlogSearcher
{
const string Site = "http://yourblog.com";
public static List<SearchResult> Get(string searchTerms, int count=10)
{
var searchResults = new List<SearchResult>();
var client = new RestSharp.RestClient(Site);
//note 10 is the page size for the search results
var pages = (int)Math.Ceiling((double)count/10);
for (int page = 1; page <= pages; page++)
{
var request = new RestSharp.RestRequest
{
Method = Method.GET,
//the part after .com/
Resource = "page/" + page
};
//Your search params here
request.AddParameter("s", HttpUtility.UrlEncode(searchTerms));
var res = client.Execute(request);
searchResults.AddRange(ParseHtml(res.Content));
}
return searchResults;
}
public static List<SearchResult> ParseHtml(string html)
{
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(html);
var results = doc.DocumentNode.QuerySelectorAll("#content-main > div");
var searchResults = new List<SearchResult>();
foreach(var node in results)
{
bool add = false;
var sr = new SearchResult();
var a = node.QuerySelector(".posttitle > h2 > a");
if (a != null)
{
add = true;
sr.Title = a.InnerText;
sr.Link = a.Attributes["href"].Value;
}
var p = node.QuerySelector(".entry > p");
if (p != null)
{
add = true;
sr.Exceprt = p.InnerText;
}
if(add)
searchResults.Add(sr);
}
return searchResults;
}
}
public class SearchResult
{
public string Title { get; set; }
public string Link { get; set; }
public string Exceprt { get; set; }
}
}
Good luck,
Eric
In my web app I use several asmx (Web Services) from the same provider, they have one for this, other for that, but all require a SOAP Header with Authentication.
It is simple to add the Authentication:
public static SoCredentialsHeader AttachCredentialHeader()
{
SoCredentialsHeader ch = new SoCredentialsHeader();
ch.AuthenticationType = SoAuthenticationType.CRM5;
ch.UserId = "myUsername";
ch.Secret = apUtilities.CalculateCredentialsSecret(
SoAuthenticationType.CRM5, apUtilities.GetDays(), "myUsername", "myPassword");
return ch;
}
The problem is this SoCredentialsHeader come (derivation) from ONE webservice and I need to add the same code to the others, like:
public static wsContact.SoCredentialsHeader AttachContactCredentialHeader()
{
wsContact.SoCredentialsHeader ch = new wsContact.SoCredentialsHeader();
ch.AuthenticationType = wsContact.SoAuthenticationType.CRM5;
ch.UserId = "myUsername";
ch.Secret = apUtilities.CalculateCredentialsSecret(
wsContact.SoAuthenticationType.CRM5, apUtilities.GetDays(), "myUsername", "myPassword");
return ch;
}
public static wsDiary.SoCredentialsHeader AttachDiaryCredentialHeader()
{
wsDiary.SoCredentialsHeader ch = new wsDiary.SoCredentialsHeader();
ch.AuthenticationType = wsDiary.SoAuthenticationType.CRM5;
ch.UserId = "myUsername";
ch.Secret = apUtilities.CalculateCredentialsSecret(
wsDiary.SoAuthenticationType.CRM5, apUtilities.GetDays(), "myUsername", "myPassword");
return ch;
}
Is there a way to implement a design pattern in order to use only one function but that suits all webServices?
sometimes I see the T letter, is this a case for that? if yes, how can I accomplish such feature?
P.S. I could pass an enum and use a switch to check the enum name and apply the correct Header but everytime I need to add a new WebService, I need to add the enum and the code, I'm searcging for an advanced technique for this.
Thank you.
Create a file called whatever.tt (the trick is the .tt extension) anywhere in your VS solution and paste the following code:
using System;
namespace Whatever
{
public static class Howdy
{
<#
string[] webServices = new string[] {"wsContact", "wsDiary"};
foreach (string wsName in webServices)
{
#>
public static <#=wsName#>.SoCredentialsHeader AttachContactCredentialHeader()
{
<#=wsName#>.SoCredentialsHeader ch = new <#=wsName#>.SoCredentialsHeader();
ch.AuthenticationType = <#=wsName#>.SoAuthenticationType.CRM5;
ch.UserId = "myUsername";
ch.Secret = apUtilities.CalculateCredentialsSecret(<#=wsName#>.SoAuthenticationType.CRM5,
apUtilities.GetDays(), "myUsername", "myPassword");
return ch;
}
}
<# } #>
}
Then watch as a whatever.cs magically appears with the desired code snippets.These are called T4 templates for code generation in VS.
You'll want to turn these into partial classes or extension methods or something. The above code will not function "as is" but you get the idea.
I don't know if this is something you wish to consider, as it definitely has it's down sides, but - as ultimately these ( the varoius SoCredentialsHeader classes) are all copies of the same class definition in different namespaces so with a little bit of refactoring you could simply have one class and one method.
Copy the SoCredentialsHeader class definition to a project of your own, add a reference to it and remove the class definition from all web service's proxies.
Add a using statement at the top of the proxy code file and it would not tell the difference.
Basically you told it to use the same class definition (yours) for all web services.
The obvious down side is that you have to repeat this exercise whenever you update and web service reference (and that it assumes all services keep using the same definition), but we've been doing this in a similar scenario and it worked quite well for us.
I would try using a generic method, then use reflection to set the properties:
public static T AttachDiaryCredentialHeader<T>() where T: class
{
T ch = new T();
Type objType = ch.GetType();
PropertyInfo userId = objType.GetProperty("UserId");
authType.SetValue(ch, "myUsername", null)
//And so on for the other properties...
return ch;
}
IMHO, This is somewhat hacky, I would keep them separate, unless like the previous post mentioned it, you're absolutely sure the definitions of these services will remain the same. One minor change to one of them will break this.