How to get the names of the namespaces loaded at the beginning - c#

How to get the names of the namespaces loaded at the beginning of a C# file? For example, get the six namespace names below.
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Text;
using System.Reflection;
namespace MyNM
{
class MyClass{}
}

This returns all the assemblies references by the executed assembly.
Still, this will NOT return only the namespaces used within a specific file - which is impossible at runtime.
var asms = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
foreach (var referencedAssembly in asms)
{
Console.WriteLine(referencedAssembly.Name);
}
Although, you can, technically, if you know the name of the file which contains your current code, simply read the file at runtime and extract the "using's".
EDIT
The way to do this would be:
public static IEnumerable<string> GetUsings()
{
// Gets the file name of the caller
var fileName = new StackTrace(true).GetFrame(1).GetFileName();
// Get the "using" lines and extract and full namespace
return File.ReadAllLines(fileName)
.Select(line => Regex.Match(line, "^\\s*using ([^;]*)"))
.Where(match => match.Success)
.Select(match => match.Groups[1].Value);
}

How to get the names of the namespaces loaded at the beginning of a C# file? For example, get the six namespace names below.
You can't, other than parsing the file yourself (or using something like the Roslyn CTP to parse a C# file).
Namespaces aren't "loaded" - they are only used by the compile, at compile time, to resolve the appropriate type names.
You can use Assembly.GetReferencedAssemblies to get the assemblies referenced by your assembly (ie: project), but this is an assembly wide set of references, and distinctly different than the namespace using directives included in a specific file.

You have to parse the file to derive the Syntax elements. As mentioned above, you can use System.Reflections for external references or you can use the new Roslyn compiler service as below
string codeSnippet = #"using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Text;
using System.Reflection;
namespace MyNM
{
class MyClass{}
}";
//APPROACH:
//Get using statements in the code snippet from the syntax tree
//find qualified name(eg:System.Text..) in the using directive
SyntaxTree tree = SyntaxTree.ParseCompilationUnit(codeSnippet);
SyntaxNode root = tree.GetRoot();
IEnumerable<UsingDirectiveSyntax> usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>();
foreach(UsingDirectiveSyntax usingDirective in usingDirectives){
NameSyntax ns = usingDirective.Name;
Console.WriteLine(ns.GetText());
}
NOTE: The code uses older version of Roslyn API. It may break in future as Roslyn is still a CTP.

Related

How to use referenced c# dll code in a VS C# console application

So I have two dlls, Algorithms.dll and Data_Structures.dll (I made these from projects I found on GitHub). Using the browse feature I have managed to add both of the DLL files as references to my Visual Studio 2017 console project. The problem is I can't do anything else with them. Whenever I try to reference something within either file, it simply cannot be found. The only thing that is recognized is the namespace, but nothing inside of that.
What do I need to do to get VS to find the classes these DLLs contain so I can use them? I am aware I need to use Algorithms.Sorting for the example but I can't call anything so I used this as an example.
P.S. If you need more info, please ask. I'm not sure what's relevant to this issue.
EDIT: Ok, it was misleading to have that kind of example. Corrected but please read the question.
EDIT: I tried this on Monodevelop and get the same issue. Maybe it's not the IDE that's the problem?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Algorithms.Sorting; // Error, Sorting cannot be found, and neither can the file container Sorting
using Data_Structures; //Perfectly ok, can find the namespace
namespace CS_HW2_Testing_App
{
class Program
{
static void Main(string[] args)
{
// I'd like to call MergeSort and so forth here. What am I missing?!
}
}
}
Here's the top piece of the file containing MergeSort if it helps
using System;
using System.Collections.Generic;
using Algorithms.Common;
namespace Algorithms.Sorting
{
public static class MergeSorter
{
//
// Public merge-sort API
public static List<T> MergeSort<T>(this List<T> collection, Comparer<T> comparer = null)
{
comparer = comparer ?? Comparer<T>.Default;
return InternalMergeSort(collection, 0, collection.Count - 1, comparer);
}
...
In the first code block, you're importing the wrong namespace: using Algorithms.MergeSort should be using Algorithms.Sorting. Then you can use MergeSorter.MergeSort<T>(...) in your code!
You need to reference the namespace not the class.
using Algorithms.Sorting; //instead of using Algorithms.MergeSort;
Plus make sure the classes are public

How can I detect unused imports in a Script (rather than a Document) with Roslyn?

I'm writing a system to process snippets written as unit tests for Noda Time, so I can include the snippets in the documentation. I've got a first pass working, but I wanted to tidy up the code. One of the things this needs to do when processing a snippet is work out which of the using directives are actually required for that snippet. (There can be multiple snippets in a single source file, but each snippet will appear separately in the documentation - I don't want imports from one snippet affecting another.)
The working code deals with Document instances - I create a separate Document per snippet containing a single method and all the potential imports, add it to the project, and then remove unnecessary using directives like this:
private async static Task<Document> RemoveUnusedImportsAsync(Document document)
{
var compilation = await document.Project.GetCompilationAsync();
var tree = await document.GetSyntaxTreeAsync();
var root = tree.GetRoot();
var unusedImportNodes = compilation.GetDiagnostics()
.Where(d => d.Id == "CS8019")
.Where(d => d.Location?.SourceTree == tree)
.Select(d => root.FindNode(d.Location.SourceSpan))
.ToList();
return document.WithSyntaxRoot(
root.RemoveNodes(unusedImportNodes, SyntaxRemoveOptions.KeepNoTrivia));
}
I've since learned that I could use the IOrganizeImportsService when working with a document, but I'd like to just write it as a Script, as that feels much cleaner in various ways.
Creating the script is easy, so I'd like to just analyze that for unused imports (after some earlier cleanup steps). Here's code I'd hoped would work for a script:
private static Script RemoveUnusedImports(Script script)
{
var compilation = script.GetCompilation();
var tree = compilation.SyntaxTrees.Single();
var root = tree.GetRoot();
var unusedImportNodes = compilation.GetDiagnostics()
.Where(d => d.Id == "CS8019")
.Where(d => d.Location?.SourceTree == tree)
.Select(d => root.FindNode(d.Location.SourceSpan))
.ToList();
var newRoot = root.RemoveNodes(unusedImportNodes, SyntaxRemoveOptions.KeepNoTrivia);
return CSharpScript.Create(newRoot.ToFullString(), script.Options);
}
Unfortunately, that doesn't find any diagnostics at all - they're just not produced in the compilation :(
Here's a short sample app demonstrating that:
using System;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
class Program
{
static void Main(string[] args)
{
string text = #"
using System;
using System.Collections.Generic;
Console.WriteLine(""I only need to use System"");";
Script script = CSharpScript.Create(text);
// Not sure whether this *should* be required, but it doesn't help...
script.Compile();
var compilation = script.GetCompilation();
foreach (var d in compilation.GetDiagnostics())
{
Console.WriteLine($"{d.Id}: {d.GetMessage()}");
}
}
}
Required package: Microsoft.CodeAnalysis.CSharp.Scripting (e.g. v2.1.0)
This produces no output :(
My guess is that this is intended, because scripting usually has different use cases. But is there any way of enabling more diagnostics for scripting purposes? Or is there some alternative way of detecting unused imports in a Script? If not, I'll go back to my Document-based approach - which would be a pity, as everything else seems to work quite nicely with scripts...
As far as I know, the default compilation in the scripting engine doesn't configure diagnostics for anything but syntax errors. Unfortunately the scripting engine only has limited options to configure the underlying compilation yourself.
However, you can probably achieve what you're after by skipping the scripting engine and directly creating the compilation yourself. This is essentially what the script host does behind the scenes with the addition of some of the defaults for the compilation as well as a few fancy things like lifting class declarations. The code to skip the script host and create the compilation yourself would look something like:
using System;
using System.IO;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
class Program
{
static void Main(string[] args)
{
string text = #"
using System;
using System.Collections.Generic;
Console.WriteLine(""I only need to use System"");";
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(text, new CSharpParseOptions(kind: SourceCodeKind.Script));
var coreDir = Path.GetDirectoryName(typeof(object).GetTypeInfo().Assembly.Location);
var mscorlib = MetadataReference.CreateFromFile(Path.Combine(coreDir, "mscorlib.dll"));
var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
var compilation = CSharpCompilation.Create("MyAssembly")
.AddSyntaxTrees(syntaxTree)
.AddReferences(mscorlib)
.WithOptions(options);
foreach (var d in compilation.GetDiagnostics())
{
Console.WriteLine($"{d.Id}: {d.GetMessage()}");
}
}
}
You'll notice this produces some undesirable diagnostics about missing references and such - the compilation references need to be tweaked a little to include the default libraries (you can see the pattern with mscorlib above). You should see the desired diagnostics about unused using statements as well.

Namespace not found error when compiling assembly from files

What I'm trying to achieve is generate a project dynamically from c# classes generated by me.
This classes' content are a similar content of code-first code generation of entity framework.The content looks as follows:
namespace ElasticTables
{
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations.KeyAttribute;
[Table("address")]
public partial class address
{
[Key]
public decimal id { get; set; }
public string name { get; set; }
}
}
I generate this files from the tables in my database, and then try to compile it programmatically so that I can reference the generated project in another project that works with an API.
The main errors while compiling is:
The type or namespace name 'KeyAttribute' does not exist in the namespace 'System.ComponentModel.DataAnnotations' (are you missing an assembly reference?)
The type or namespace 'Key' could not be found
The type or namespace 'Table' could not be found.
I'm using 'CSharpCodeProvider'
var provider = new CSharpCodeProvider();
var options = new CompilerParameters
{
OutputAssembly = "ElasticTables.dll",
CompilerOptions = "/optimize"
};
And I have the following referenced Assemblies
options.ReferencedAssemblies.Add(Directory.GetCurrentDirectory() + "\\EntityFramework.dll");
options.ReferencedAssemblies.Add(Directory.GetCurrentDirectory() + "\\EntityFramework.SqlServer.dll");
I have an string array with the files' paths called sources, and I try to compile with the following line
CompilerResults results = provider.CompileAssemblyFromFile(options, sources);
Help is much appreciated.
You need to reference all needed assemblies (as the error says), so you need to add, I'd say at the very least:
options.ReferencedAssemblies.Add("System.dll");
options.ReferencedAssemblies.Add("System.ComponentModel.DataAnnotations.dll");
Others might be needed
About the comment in your question, yes, you should specify options.OutputAssembly
Also, in your generated code:
using System.ComponentModel.DataAnnotations.KeyAttribute;
KeyAttribute is not a namespace, so it'll probably give an error when compiling.
I'd also take the usings before the namespace. This is not strictly needed and not an error, but it's the common practice (and that way you are sure the referenced assemblies are from the global namespace, and not childs of the namespace your class is in [just in case there's a name conflict])
Have you tried to add also a reference to "System.dll" and "System.ComponentModel.DataAnnotations.dll" (for the System.ComponentModel stuff) ?
(as you may indeed be missing an assembly reference)
options.ReferencedAssemblies.Add(
Path.Combine(
Directory.GetCurrentDirectory(),
"System.ComponentModel.DataAnnotations.dll"));

Executing .R file in C# using R.net in visual studio

I have a .r file which fetches data from database, performs some calculation and write back to a new table in database.
I am trying to execute the .r file from C# using visual studio 2010.
The C# code is mentioned below.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using RDotNet;
using RDotNet.Devices;
using RDotNet.Internals;
namespace EmbeddAssembly
{
class Program
{
static void Main(string[] args)
{
string rhome = System.Environment.GetEnvironmentVariable("R_HOME");
if (string.IsNullOrEmpty(rhome))
rhome = #"C:\Program Files\R\R-2.14.1";
System.Environment.SetEnvironmentVariable("R_HOME", rhome);
System.Environment.SetEnvironmentVariable("PATH", System.Environment.GetEnvironmentVariable("PATH") + ";" + rhome + #"binx64");
// Set the folder in which R.dll locates.
//REngine.SetDllDirectory(#"C:Program FilesRR-2.12.0bini386″);
REngine.SetDllDirectory(#"C:\Program Files\R\R-2.14.1\bin\x64");
// REngine e = REngine.CreateInstance("test", new[] { "" });
using (REngine engine = REngine.CreateInstance("RDotNet", new[] { "-q" })) // quiet mode
{
foreach (string path in engine.EagerEvaluate(".libPaths()").AsCharacter())
{
Console.WriteLine(path);
}
engine.Evaluate(".libPaths(C:\\Program Files\\R\\R-2.14.1\\library)");
engine.Evaluate("source(C:\\Users\\..\\Documents\\Visual Studio 2010\\Projects\\EmbeddAssembly\\multi.r)");
Console.ReadLine();
}
}
}
}
I am not getting any error but it is not calling the .r file.
The code in r file works fine because I am able to retrive data and write into the table. However on calling that from C# it is not performing any action.
You are missing character delimitors around arguments to the R functions called. Also, avoid using backslashes when passing strings to R.NET's Evaluate. You may need to end up having quadruple backslashes (or even more if using regular expressions) to get things to work.
You should use something like:
engine.Evaluate(".libPaths('C:/Program Files/R/R-2.14.1/library')");
engine.Evaluate("source('C:/Users/Documents/Visual Studio 2010/Projects/EmbeddAssembly/multi.r')");
It seems you are using a very old version of R.NET; I strongly advise you to use R.NET.Community on NuGet. I know there is an R.NET nuget feed still up too, but this appears not maintained anymore. Also, FYI, R.NET latest reference documentation is now on a GitHub page

Less specific using statements conflicting with more specific ones

Can anyone explain this behavior and what the solution is?
I installed Ninject.MVC3 via nuget, this creates a file in the App_Start folder called NinjectWebCommon.cs with the namespace like this:
namespace MvcApplication1.App_Start {
...
using Ninject;
using Ninject.Web.Common;
...
}
Now, I want to create an NinjectModule and am having some issues with the Ninject namespace being recognized.
namespace MvcApplication1.Ninject.Modules {
using Ninject.Modules
...
}
As soon as I add the using statement in the module, NinjectWebCommon.cs can no longer compile. If I place the using outside the namespace, it still won't compile.
If, however, i change the namespace for my module to MvcApplication1.Foo, then it works fine either way.
Why would naming this MvcApplication1.Ninject.Modules (or even just MvcApplication1.Ninject) cause NinjectWebCommon.cs to no longer find it's references? I thought the purpose of namespaces was to prevent this sort of thing?
Using statements within a namespace search in the child namespace of the current namespace and all its ancestors before looking at the global name space. E.g. If you have a namespace MvcApplication1.A you can write
using A
Instead of
using MvcApplication1.A
Because of this, your example is interpreted by the compiler as
namespace MvcApplication1.Ninject.Modules {
using MvcApplication1.Ninject.Modules
    ...
}
You can force that the compiler looks only in the global namespace like this:
namespace MvcApplication1.Ninject.Modules {
using global::Ninject.Modules
    ...
}

Categories

Resources