Roslyn GetDescriptionAsync gives incomplete description - c#

I am trying to explore Roslyn's code completion service. I am able to get the completion list, but when I try to get the description it only returns the type and access modifiers, but not the actual description (e.g. Function used for printing to the console).
This is how I tried getting the description:
var code = #"using System;
using TestDll;
public class MyClass
{
public static void MyMethod(int value)
{
Test.
}
}";
var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), "MyProject", "MyProject", LanguageNames.CSharp).
WithMetadataReferences(new[] { MetadataReference.CreateFromFile("TestDll.dll", documentation: XmlDocumentationProvider.CreateFromFile("TestDLL.xml")) });
var project = workspace.AddProject(projectInfo);
var document = workspace.AddDocument(project.Id, "MyFile.cs", SourceText.From(code));
await PrintCompletionResults(document, code.LastIndexOf("Test.") + 5);
PrintCompletion Method
private static async Task PrintCompletionResults(Document document, int position)
{
var completionService = CompletionService.GetService(document);
var results = await completionService.GetCompletionsAsync(document, position);
foreach (var i in results.Items)
{
Console.WriteLine(i.DisplayText);
var desc = completionService.GetDescriptionAsync(document, i).Result; // returns for example "interface Test.DebugPrint()"
Console.WriteLine(desc.Text);
foreach (var prop in i.Properties)
{
Console.Write($"{prop.Key}:{prop.Value} ");
}
Console.WriteLine();
foreach (var tag in i.Tags)
{
Console.Write($"{tag} ");
}
Console.WriteLine();
Console.WriteLine();
}
}

Related

How to properly access object's List<> value in C#?

I am trying to get the object value but I don't know how to do it. I'm new to C# and its giving me syntax error. I want to print it separately via the method "PrintSample" How can I just concatenate or append the whatData variable . Thank you.
PrintSample(getData, "name");
PrintSample(getData, "phone");
PrintSample(getData, "address");
//Reading the CSV file and put it in the object
string[] lines = File.ReadAllLines("sampleData.csv");
var list = new List<Sample>();
foreach (var line in lines)
{
var values = line.Split(',');
var sampleData = new Sample()
{
name = values[0],
phone = values[1],
address = values[2]
};
list.Add(sampleData);
}
public class Sample
{
public string name { get; set; }
public string phone { get; set; }
public string adress { get; set; }
}
//Method to call to print the Data
private static void PrintSample(Sample getData, string whatData)
{
//THis is where I'm having error, how can I just append the whatData to the x.?
Console.WriteLine( $"{getData. + whatData}");
}
In C# it's not possible to dynamically evaluate expression like
$"{getData. + whatData}"
As opposed to languages like JavaScript.
I'd suggest to use rather switch expression or Dictionary<string, string>
public void PrintData(Sample sample, string whatData)
{
var data = whatData switch
{
"name" => sample.name,
"phone" => sample.phone,
"address" => sample.address
_ => throw new ArgumentOutOfRangeException(nameof(whatData)),
};
Console.WriteLine(data);
}
I'm not sure what you are trying to achieve. Perhaps this will help you:
private static void PrintSample(Sample getData, string whatData)
{
var property = getData.GetType().GetProperty(whatData);
string value = (string)property?.GetValue(getData) ?? "";
Console.WriteLine($"{value}");
}
What PO really needs is
private static void PrintSamples(List<Sample> samples)
{
foreach (var sample in samples)
Console.WriteLine($"name : {sample.name} phone: {sample.phone} address: {sample.address} ");
}
and code
var list = new List<Sample>();
foreach (var line in lines)
{
......
}
PrintSamples(list);
it is radicolous to use
PrintSample(getData, "name");
instead of just
PrintSample(getData.name)
You can do this using reflection. However, it's known to be relatively slow.
public static void PrintSample(object getData, string whatData)
{
Console.WriteLine( $"{getData.GetType().GetProperty(whatData).GetValue(getData, null)}");
}

IOS 14 request limited photo access

I'm trying to use PHPickerController and access PHAsset to get file name and file size but the PHAsset are null
var config = new PHPickerConfiguration(PHPhotoLibrary.SharedPhotoLibrary) {
Filter = PHPickerFilter.ImagesFilter,
SelectionLimit = 1
};
var picker= new PHPickerViewController(config) {
ModalPresentationStyle = UIModalPresentationStyle.Popover,
Delegate = new ImagePickerDelegate((fileSize, fileName, url) => {
})
};
ViewController.PresentViewController(picker, true, null);
public class ImagePickerDelegate : PHPickerViewControllerDelegate
{
public ImagePickerDelegate(Action<int, string, string> action)
{
Action = action;
}
public Action<int, string, string> Action { get; }
public override void DidFinishPicking(PHPickerViewController picker, PHPickerResult[] results)
{
picker.DismissViewController(true, null);
foreach (var result in results)
{
var asset = PHAsset.FetchAssets(result.AssetIdentifier, null)?.firstObject as PHAsset;
// The asset are null
var fileSize = asset.ValueForKey((NSString)"fileSize");
}
}
}
As you can see in the image the request dialog show and code are not pause on following line
var asset = PHAsset.FetchAssets(result.AssetIdentifier, null)?.firstObject as PHAsset;
and return null
You could use FetchAssetsUsingLocalIdentifiers method to get PHAsset object, then it will return value.
Sample code as follows:
public override void DidFinishPicking(PHPickerViewController picker, PHPickerResult[] results)
{
picker.DismissViewController(true, null);
foreach (var result in results)
{
var refID = result.AssetIdentifier;
string[] refIDs = new string[] { refID };
var asset = PHAsset.FetchAssetsUsingLocalIdentifiers(refIDs, null)?.firstObject as PHAsset;
// var fileSize = asset.ValueForKey((NSString)"fileSize");
}
}
Also could have a look at this native code link.

how can I access values stored within a method from other places of a class [duplicate]

This question already has answers here:
Help someone new to C# variables
(5 answers)
Closed 2 years ago.
In my current application while I have been able to implement the required logic that I need I am really stuck when trying to take off the content from the main method and using it from a different method .
My code is as below,
class Program
{
const string path = #"filePath";
static void Main(string[] args)
{
setUpValues();
}
private static void setUpValues()
{
var Content = JsonConvert.DeserializeObject<deploy>(File.ReadAllText(path));
List<Variable> variables = Content.Variables.ToList();
Scopes Scope = Content.ScopeValues;
string Version = null;
List<string> ListOfSelectedItems= new List<string>();
List<string> TempListOfSelectedItems = new List<string>();
List<string> Channels = new List<string>();
foreach (var item in variables)
{
if (item.Name.Equals("version"))
{
Version = item.Value;
}
if (item.Name.Equals("Selected"))
{
TempListOfSelectedItems.Add(item.Value);
}
}
Console.WriteLine("Version " + Version);
Console.WriteLine();
string SelectedItems= TempListOfSelectedItems[0];
ListOfSelectedItems = SelectedItems.Split(',').ToList();
Console.WriteLine();
Console.WriteLine("Selected Modules");
Console.WriteLine();
foreach (var item in ListOfSelectedItems)
{
Console.WriteLine(item);
}
foreach (var item in Scope.Channels)
{
Channels.Add(item.Name);
}
}
}
I want to be able to access the variable string Version , the List of ListOfSelectedItems and the List of channels from outside this method .. I want to use these in another as well . So how can I make these globally accessible ?
Would really appreciate your help on this as I have been stuck here
In order to use variables outside a method, you should declare them as fields of a class. Like this:
class Program
{
const string path = #"filePath";
static deploy Content;
static string Version;
static List<string> ListOfSelectedItems;
static List<string> TempListOfSelectedItems;
static List<string> Channels;
// and others
static void Main(string[] args)
{
setUpValues();
}
private static void setUpValues()
{
Content = JsonConvert.DeserializeObject<deploy>(File.ReadAllText(path));
List<Variable> variables = Content.Variables.ToList();
Scopes Scope = Content.ScopeValues;
Version = null;
ListOfSelectedItems = new List<string>();
TempListOfSelectedItems = new List<string>();
Channels = new List<string>();
foreach (var item in variables)
{
if (item.Name.Equals("version"))
{
Version = item.Value;
}
if (item.Name.Equals("Selected"))
{
TempListOfSelectedItems.Add(item.Value);
}
}
Console.WriteLine("Version " + Version);
Console.WriteLine();
string SelectedItems = TempListOfSelectedItems[0];
ListOfSelectedItems = SelectedItems.Split(',').ToList();
Console.WriteLine();
Console.WriteLine("Selected Modules");
Console.WriteLine();
foreach (var item in ListOfSelectedItems)
{
Console.WriteLine(item);
}
foreach (var item in Scope.Channels)
{
Channels.Add(item.Name);
}
}
}
You have to declare those fields as static because they are used in a static method. After the setUpValues finishes running, you can use those fields inside the Main method as well.
Also, this is not related to the question, but the general code convention in C# is to start methods' names with an uppercase letter (so SetUpValues instead of setUpValues) and to start the local variables' names with a lowercase letter (selectedItems instead of SelectedItems). Obviously, it's ultimately up to you how to name things and which code convention to use.
Create a class with properties that you want to access from other places. Instantiate this class in setUpValues and return this.
public class TestClass
{
public TestClass()
{
this.ListOfSelectedItems = new List<string>();
}
public string Version { get; set; }
public List<string> ListOfSelectedItems { get; set; }
}
And then modify your Main method as:
var myObj = setUpValues();
And then Modify setUpValues to return this:
private static TestClass setUpValues()
{
var Content = JsonConvert.DeserializeObject<deploy>(File.ReadAllText(path));
List<Variable> variables = Content.Variables.ToList();
Scopes Scope = Content.ScopeValues;
string Version = null;
List<string> ListOfSelectedItems = new List<string>();
List<string> TempListOfSelectedItems = new List<string>();
List<string> Channels = new List<string>();
foreach (var item in variables)
{
if (item.Name.Equals("version"))
{
Version = item.Value;
}
if (item.Name.Equals("Selected"))
{
TempListOfSelectedItems.Add(item.Value);
}
}
var retObj = new TestClass();
Console.WriteLine("Version " + Version);
Console.WriteLine();
retObj.Version = Version;
string SelectedItems = TempListOfSelectedItems[0];
ListOfSelectedItems = SelectedItems.Split(',').ToList();
Console.WriteLine();
Console.WriteLine("Selected Modules");
Console.WriteLine();
foreach (var item in ListOfSelectedItems)
{
Console.WriteLine(item);
retObj.ListOfSelectedItems.Add(item);
}
foreach (var item in Scope.Channels)
{
Channels.Add(item.Name);
}
return retObj;
}

Seperation of db connection in seperate class file doesn't work

Rookie here needing help. I'm trying to build a prototype with the neo4j .NET driver using Bolt. My aim with the prototype is building multiple methods for creation and searches in the db, but only one method to connect to the db - here I'm continuously having problems. I've Googled all weekend for examples, tutorials and traversed through the documentation and now I need your help.
Programs.cs
using System;
using DTUneo4jConsoleApp.Db;
namespace DTUneo4jConsoleApp
{
public class Program
{
public static void Main(string[] args)
{
MyProperties something = new MyProperties();
neo4jdb session = new neo4jdb();
session.Run($"CREATE (a:Person {{name:'{something.Name}', title:'{something.Title}'}})");
var result = session.Run($"MATCH (a:Person) WHERE a.name = '{something.Name}' RETURN a.name AS name, a.title AS title");
foreach (var record in result)
{
Console.WriteLine($"{record["title"].As<string>()} {record["name"].As<string>()}");
}
Console.ReadKey();
}
}
public class MyProperties
{
public string Name { get; set; }
public string Title { get; set; }
}
}
db.cs
using Neo4j.Driver.V1;
namespace DTUneo4jConsoleApp.Db
{
public class neo4jdb
{
public static void Connection()
{
using (var driver = GraphDatabase.Driver("bolt://localhost", AuthTokens.Basic("user", "pass")))
using (var session = driver.Session())
{
}
}
}
}
When I instantiate the neo4jdb session = new neo4jdb(); I don't get i.e. the Run() method from the driver.
I hope someone can guide me in the right direction.
I am doing it like this:
public static List<IStatementResult> ExecuteCypher(List<Statement> statements)
{
List<IStatementResult> results = new List<IStatementResult>();
using (var driver = GraphDatabase.Driver("bolt://localhost", AuthTokens.Basic("user", "pass")))
{
using (var session = driver.Session())
{
using (var tx = session.BeginTransaction())
{
foreach (var statement in statements)
{
results.Add(tx.Run(statement));
}
tx.Success();
}
}
}
return results;
}
usage:
MyProperties something = new MyProperties();
var createCypher = new Statement($"CREATE (a:Person {{name:'{something.Name}', title:'{something.Title}'}})");
var matchCypher = new Statement($"MATCH (a:Person) WHERE a.name = '{something.Name}' RETURN a.name AS name, a.title AS title");
var statements = new List<Statement>();
statements.Add(createCypher);
statements.Add(matchCypher);
var results = ExecuteCypher(statements);
//you can now query result for each statement or
//your query your desired result
foreach (var record in results.Last())
{
Console.WriteLine($"{record["title"].As<string>()} {record["name"].As<string>()}");
}
In this way I can also create multiple records in a single transaction and get the result of all those as well.

Replacing a method node using Roslyn

While exploring Roslyn I put together a small app that should include a trace statement as the first statement in every method found in a Visual Studio Solution. My code is buggy and is only updating the first method.
The line that is not working as expected is flagged with a “TODO” comment. Please, advise.
I also welcome style recommendations that would create a more streamlined/readable solution.
Thanks in advance.
...
private void TraceBtn_Click(object sender, RoutedEventArgs e) {
var myWorkSpace = new MyWorkspace("...Visual Studio 2012\Projects\Tests.sln");
myWorkSpace.InjectTrace();
myWorkSpace.ApplyChanges();
}
...
using System;
using System.Linq;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;
namespace InjectTrace
{
public class MyWorkspace
{
private string solutionFile;
public string SolutionFile {
get { return solutionFile; }
set {
if (string.IsNullOrEmpty(value)) throw new Exception("Invalid Solution File");
solutionFile = value;
}
}
private IWorkspace loadedWorkSpace;
public IWorkspace LoadedWorkSpace { get { return loadedWorkSpace; } }
public ISolution CurrentSolution { get; private set; }
public IProject CurrentProject { get; private set; }
public IDocument CurrentDocument { get; private set; }
public ISolution NewSolution { get; private set; }
public MyWorkspace(string solutionFile) {
this.SolutionFile = solutionFile;
this.loadedWorkSpace = Workspace.LoadSolution(SolutionFile);
}
public void InjectTrace()
{
int projectCtr = 0;
int documentsCtr = 0;
int transformedMembers = 0;
int transformedClasses = 0;
this.CurrentSolution = this.LoadedWorkSpace.CurrentSolution;
this.NewSolution = this.CurrentSolution;
//For Each Project...
foreach (var projectId in LoadedWorkSpace.CurrentSolution.ProjectIds)
{
CurrentProject = NewSolution.GetProject(projectId);
//..for each Document in the Project..
foreach (var docId in CurrentProject.DocumentIds)
{
CurrentDocument = NewSolution.GetDocument(docId);
var docRoot = CurrentDocument.GetSyntaxRoot();
var newDocRoot = docRoot;
var classes = docRoot.DescendantNodes().OfType<ClassDeclarationSyntax>();
IDocument newDocument = null;
//..for each Class in the Document..
foreach (var #class in classes) {
var methods = #class.Members.OfType<MethodDeclarationSyntax>();
//..for each Member in the Class..
foreach (var currMethod in methods) {
//..insert a Trace Statement
var newMethod = InsertTrace(currMethod);
transformedMembers++;
//TODO: PROBLEM IS HERE
newDocRoot = newDocRoot.ReplaceNode(currMethod, newMethod);
}
if (transformedMembers != 0) {
newDocument = CurrentDocument.UpdateSyntaxRoot(newDocRoot);
transformedMembers = 0;
transformedClasses++;
}
}
if (transformedClasses != 0) {
NewSolution = NewSolution.UpdateDocument(newDocument);
transformedClasses = 0;
}
documentsCtr++;
}
projectCtr++;
if (projectCtr > 2) return;
}
}
public MethodDeclarationSyntax InsertTrace(MethodDeclarationSyntax currMethod) {
var traceText =
#"System.Diagnostics.Trace.WriteLine(""Tracing: '" + currMethod.Ancestors().OfType<NamespaceDeclarationSyntax>().Single().Name + "." + currMethod.Identifier.ValueText + "'\");";
var traceStatement = Syntax.ParseStatement(traceText);
var bodyStatementsWithTrace = currMethod.Body.Statements.Insert(0, traceStatement);
var newBody = currMethod.Body.Update(Syntax.Token(SyntaxKind.OpenBraceToken), bodyStatementsWithTrace,
Syntax.Token(SyntaxKind.CloseBraceToken));
var newMethod = currMethod.ReplaceNode(currMethod.Body, newBody);
return newMethod;
}
public void ApplyChanges() {
LoadedWorkSpace.ApplyChanges(CurrentSolution, NewSolution);
}
}
}
The root problem of you code is that newDocRoot = newDocRoot.ReplaceNode(currMethod, newMethod); somehow rebuilds newDocRoot internal representation of code so next currMethod elements won't be find in it and next ReplaceNode calls will do nothing. It is a situation similar to modifying a collection within its foreach loop.
The solution is to gather all necessary changes and apply them at once with ReplaceNodes method. And this in fact naturally leads to simplification of code, because we do not need to trace all those counters. We simply store all needed transformation and apply them for whole document at once.
Working code after changes:
public void InjectTrace()
{
this.CurrentSolution = this.LoadedWorkSpace.CurrentSolution;
this.NewSolution = this.CurrentSolution;
//For Each Project...
foreach (var projectId in LoadedWorkSpace.CurrentSolution.ProjectIds)
{
CurrentProject = NewSolution.GetProject(projectId);
//..for each Document in the Project..
foreach (var docId in CurrentProject.DocumentIds)
{
var dict = new Dictionary<CommonSyntaxNode, CommonSyntaxNode>();
CurrentDocument = NewSolution.GetDocument(docId);
var docRoot = CurrentDocument.GetSyntaxRoot();
var classes = docRoot.DescendantNodes().OfType<ClassDeclarationSyntax>();
//..for each Class in the Document..
foreach (var #class in classes)
{
var methods = #class.Members.OfType<MethodDeclarationSyntax>();
//..for each Member in the Class..
foreach (var currMethod in methods)
{
//..insert a Trace Statement
dict.Add(currMethod, InsertTrace(currMethod));
}
}
if (dict.Any())
{
var newDocRoot = docRoot.ReplaceNodes(dict.Keys, (n1, n2) => dict[n1]);
var newDocument = CurrentDocument.UpdateSyntaxRoot(newDocRoot);
NewSolution = NewSolution.UpdateDocument(newDocument);
}
}
}
}

Categories

Resources