Resharper - Go To Implementation listing reference twice - c#

In one of my solutions, when I right click a symbol and choose "Go To Implementation" for an object defined in one of the other solution projects, it lists the reference twice and forces me to choose one.
Based on the icons, it appears that one of the items in the list represents the project, and the other represents a dll. It doesn't matter which one I click - it goes to the same source file.
I only have the library reference once in this particular project - it is referencing the project.
What would cause this to happen? Some sort of circular reference issue perhaps?

As far as I can tell, this can also happen if you have a solution with several projects, where a certain project is referenced as project and also as pure file by two other projects in the solution.
Another advice that I can give if something is broken with ReSharper, is to clear the cache.

I had this problem and I just fixed it.
First, try do a Clean Solution and then a Build.
In my case, one rogue Project in my solution was compiled using an older version of the .NET framework than the other Projects, so when Resharper added a reference to my other Projects for me, it must have added it as a dll reference instead of as a Project reference.
My fix was
Upgrade old Project to the same version of .NET framework as the other Projects
Remove references to other Projects from that old Project
Add references to the other Projects again (as Project references this time)
Clean solution
Build solution
Done.

I've found a couple different cases that cause this problem, and got so annoyed that I wrote a little console app to scan my solution and find the problems for me. Here it is for anyone who might find this useful. To run it pass it the path to your solution folder and it will print out the issues on the console. It's very "quick and dirty" but it found the issues for me.
class Program
{
static void Main(string[] args)
{
if (args != null && args.Any())
{
foreach (var s in args)
{
Console.WriteLine("Checking " + s);
DirectoryInfo dir = new DirectoryInfo(s);
var files = dir.GetFiles("*.csproj", SearchOption.AllDirectories);
var projects = files.Select(x => new Project(x)).ToList();
var grouped = projects.GroupBy(x => x.TargetFrameworkVersion);
if(grouped.Count()>1)
{
Console.WriteLine("Solution contains multiple versions of Target Frameworks, this may cause duplicate assemblies in R# cache");
foreach (var group in grouped)
{
Console.WriteLine(group.Key);
foreach (var project in group)
{
Console.WriteLine(project.AssemblyName);
}
}
}
//loop through for debugging
foreach (var project in projects)
{
foreach (var reference in project.References)
{
foreach (var checkProject in projects)
{
if (checkProject.AssemblyName == reference)
{
Console.WriteLine("Reference in" + project.FileName + " referencing " +
reference+" that should be a ProjectReference, this may cause duplicate entries in R# Cache");
}
}
}
}
}
Console.WriteLine("Complete");
Console.ReadLine();
}
else
{
Console.WriteLine("You must provide a path to scan for csproj files");
}
}
}
public class Project
{
public string FileName { get; set; }
public string AssemblyName { get; set; }
public string ProjectGuid { get; set; }
public string TargetFrameworkVersion { get; set; }
public IList<string> References { get; set; }
private FileInfo _file;
private XmlDocument _document;
private XmlNamespaceManager _namespaceManager;
public Project(FileInfo file)
{
_file = file;
FileName = _file.FullName;
_document = new XmlDocument();
_document.Load(_file.FullName);
_namespaceManager = new XmlNamespaceManager(_document.NameTable);
_namespaceManager.AddNamespace("msbld", "http://schemas.microsoft.com/developer/msbuild/2003");
var projectGuidNode = _document.SelectSingleNode("//msbld:ProjectGuid", _namespaceManager);
ProjectGuid = projectGuidNode.InnerText;
var assemblyNameNode = _document.SelectSingleNode("//msbld:AssemblyName", _namespaceManager);
AssemblyName = assemblyNameNode.InnerText;
var targetFrameworkNode = _document.SelectSingleNode("//msbld:TargetFrameworkVersion", _namespaceManager);
TargetFrameworkVersion = targetFrameworkNode.InnerText;
References = new List<string>();
var referenceNodes = _document.SelectNodes("//msbld:Reference", _namespaceManager);
foreach (var node in referenceNodes)
{
var element = (XmlElement) node;
//file references
if (element.HasChildNodes)
{
foreach (var child in element.ChildNodes)
{
var childElement = (XmlElement)child;
if (childElement.Name == "HintPath")
{
var value = childElement.InnerText;
value = value.Substring(value.LastIndexOf("\\") + 1);
value = value.Replace(".dll", "");
References.Add(value);
}
}
}
//gac references
else
{
foreach (var attr in element.Attributes)
{
var attribute = (XmlAttribute)attr;
if (attribute.Name == "Include")
{
var value = attribute.Value;
string reference = value;
if (value.Contains(','))
{
reference = value.Substring(0, value.IndexOf(','));
}
References.Add(reference);
break;
}
}
}
}
}
}

Related

Get assemblyname of a project

What I need
I want to obtain the assembly name of a Project in an Visual Studio Extension.
Since I know that you can see it when right clicking on a Project in the Solution Explorer and select Properties. It will show the Assemblyname in the tab Application.
Setup
I have a "Command" in my extension project which shows when you right click something in the solution explorer. As soon as someone clicks the command the following code will execute:
var dte = await GetDTE2Async();
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
var selectedItem = GetSelectedItem(dte);
var props = selectedItem.ContainingProject.Properties;
//I just assumed that the `AssemblyName` could be somewhere in the `Properties` Property.
Where the GetSelectedItem method looks like this:
public static ProjectItem GetSelectedItem(DTE2 dte)
{
ThreadHelper.ThrowIfNotOnUIThread();
var items = (Array)dte.ToolWindows.SolutionExplorer.SelectedItems;
return items.Cast<UIHierarchyItem>().FirstOrDefault().Object as ProjectItem;
}
For anyone else who has the same issue, I actually was pretty close to my goal.
How I found it
It was just about guessing, in my case I assumed that the Properties is a bunch of Property items.
After that I just collected all the key pair values that the IEnumerable had. Then saw that there is acutally an Item called AssemblyName which indeed matches the correct value.
var items = selectedItem.ContainingProject.Properties.Cast<Property>()
var sb = new StringBuilder();
foreach (var item in items)
{
sb.Append($"Name : {item.Name} ");
try
{
sb.Append("Value : " + item.Value.ToString());
}
catch (Exception)
{
sb.Append("Value : NOT DEFINED (EXCEPTION)");
}
finally
{
sb.AppendLine();
}
}
var output = sb.ToString();
Solution
It is a fairly short part, but here we go:
var assemblyName = selectedItem.ContainingProject.Properties.Cast<Property>().FirstOrDefault(x=> x.Name == "AssemblyName").Value;
Basically I am just searching the first element which matches the name AssemblyName and get its value.
Maybe you can try:
System.Reflection.Assembly.GetAssembly(Property's Type).FullName

Find subsolution items

I am trying to find subsolution and add items into that solution programmatically. But code that i use is not searching for subfolders.
I use code on github: See code here please
Method that i search for solution is:
public static IEnumerable<EnvDTE.Project> GetAllProjects(EnvDTE.DTE dte)
{
List<EnvDTE.Project> projectList = new List<EnvDTE.Project>();
var folders = dte.Solution.Projects.Cast<EnvDTE.Project>().Where(p=>p.Kind == EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder);
foreach (EnvDTE.Project folder in folders)
{
if (folder.ProjectItems == null) continue;
foreach (EnvDTE.ProjectItem item in folder.ProjectItems)
{
if (item.Object is EnvDTE.Project)
projectList.Add(item.Object as EnvDTE.Project);
}
}
var projects = dte.Solution.Projects.Cast<EnvDTE.Project>().Where(p=>p.Kind != EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder);
if (projects.Count() > 0)
projectList.AddRange(projects);
return projectList;
}
But this is searching only main solution and sub solution. But solution that i want to find has a longer path like "MainFolder\SubFolder1\SubFolder2\SubFolder3\SubSolution"

MSBuildWorkspace Get Embedded Resource Files

I'm trying to get out all embedded resource files from a solution using Roslyn and MSBuild Api.
private async Task<Document> CheckConstForLocalization(Document document, LocalDeclarationStatementSyntax localDeclaration,
CancellationToken cancellationToken)
{
foreach (var project in document.Project.Solution.Projects)
{
foreach (var sourceDoc in project.AdditionalDocuments)
{
if (false == sourceDoc.Name.EndsWith(".cs"))
{
Debug.WriteLine(sourceDoc.Name);
}
}
foreach (var sourceDoc in project.Documents)
{
if (false == sourceDoc.Name.EndsWith(".cs"))
{
Debug.WriteLine(sourceDoc.Name);
}
}
}
var newRoot = await document.GetSyntaxRootAsync(cancellationToken);
// Return document with transformed tree.
return document.WithSyntaxRoot(newRoot);
}
When I modify my resource files to be AdditionFiles, I can get them through the project AdditionalDocuments. However I would like to be able to grab these with out doing so. The file does not appear in Documents or Additional Documents
How can I find Resx files without modifying their attributes?
I've figured out a way to find the designer files, I get the associated C# Document names by iterating over the csproj file and getting the embedded resources.
public const string LAST_GENERATED_TAG = "LastGenOutput";
public const string RESX_FILE_EXTENSION = ".resx";
public List<string> GetResourceDesignerInfo(Project project)
{
XDocument xmldoc = XDocument.Load(project.FilePath);
XNamespace msbuild = "http://schemas.microsoft.com/developer/msbuild/2003";
var resxFiles = new List<string>();
foreach (var resource in xmldoc.Descendants(msbuild + "EmbeddedResource"))
{
string includePath = resource.Attribute("Include").Value;
var includeExtension = Path.GetExtension(includePath);
if (0 == string.Compare(includeExtension, RESX_FILE_EXTENSION, StringComparison.OrdinalIgnoreCase))
{
var outputTag = resource.Elements(msbuild + LAST_GENERATED_TAG).FirstOrDefault();
if (null != outputTag)
{
resxFiles.Add(outputTag.Value);
}
}
}
return resxFiles;
}
You can't, at the moment. It isn't supported by the API. (I was researching this just yesterday.)
There's a feature request to support it which you might like to support and subscribe to, but I don't believe there's any way of doing this at the moment.
My understanding is that Visual Studio hooks into MSBuild more tightly than Roslyn's support does at the moment. (See the issue I raised about <Deterministic> for another example.)

Get access to the URL's being used in System.Web.Optimization

Background: I'm using the HTML 5 Offline App Cache and dynamically building the manifest file. Basically, the manifest file needs to list each of the static files that your page will request. Works great when the files are actually static, but I'm using Bundling and Minification in System.Web.Optimization, so my files are not static.
When in the DEBUG symbol is loaded (i.e. debugging in VS) then the actual physical files are called from the MVC View. However, when in Release mode, it calls a virtual file that could look something like this: /bundles/scripts/jquery?v=FVs3ACwOLIVInrAl5sdzR2jrCDmVOWFbZMY6g6Q0ulE1
So my question: How can I get that URL in the code to add it to the offline app manifest?
I've tried:
var paths = new List<string>()
{
"~/bundles/styles/common",
"~/bundles/styles/common1024",
"~/bundles/styles/common768",
"~/bundles/styles/common480",
"~/bundles/styles/frontend",
"~/bundles/scripts/jquery",
"~/bundles/scripts/common",
"~/bundles/scripts/frontend"
};
var bundleTable = BundleTable.Bundles;
foreach (var bundle in bundleTable.Where(b => paths.Contains(b.Path)))
{
var bundleContext = new BundleContext(this.HttpContext, bundleTable, bundle.Path);
IEnumerable<BundleFile> files = bundle.GenerateBundleResponse(bundleContext).Files;
foreach (var file in files)
{
var filePath = file.IncludedVirtualPath.TrimStart(new[] { '~' });
sb.AppendFormat(formatFullDomain, filePath);
}
}
As well as replacing GenerateBundleResponse() with EnumerateFiles(), but it just always returns the original file paths.
I'm open to alternative implementation suggestions as well. Thanks.
UPDATE: (7/7/14 13:45)
As well as the answer below I also added this Bundles Registry class to keep a list of the required static files so that it works in debug mode in all browsers. (See comments below)
public class Registry
{
public bool Debug = false;
public Registry()
{
SetDebug();
}
[Conditional("DEBUG")]
private void SetDebug()
{
Debug = true;
}
public IEnumerable<string> CommonScripts
{
get
{
if (Debug)
{
return new string[]{
"/scripts/common/jquery.validate.js",
"/scripts/common/jquery.validate.unobtrusive.js",
"/scripts/common/knockout-3.1.0.debug.js",
"/scripts/common/jquery.timepicker.js",
"/scripts/common/datepicker.js",
"/scripts/common/utils.js",
"/scripts/common/jquery.minicolors.js",
"/scripts/common/chosen.jquery.custom.js"
};
}
else
{
return new string[]{
"/scripts/common/commonbundle.js"
};
}
}
}
}
I'm by no means happy with this solution. Please make suggestions if you can improve on this.
I can suggest an alternative from this blog post create your own token.
In summary the author suggests using web essentials to create the bundled file and then creating a razor helper to generate the token, in this case based on the last changed date and time.
public static class StaticFile
{
public static string Version(string rootRelativePath)
{
if (HttpRuntime.Cache[rootRelativePath] == null)
{
var absolutePath = HostingEnvironment.MapPath(rootRelativePath);
var lastChangedDateTime = File.GetLastWriteTime(absolutePath);
if (rootRelativePath.StartsWith("~"))
{
rootRelativePath = rootRelativePath.Substring(1);
}
var versionedUrl = rootRelativePath + "?v=" + lastChangedDateTime.Ticks;
HttpRuntime.Cache.Insert(rootRelativePath, versionedUrl, new CacheDependency(absolutePath));
}
return HttpRuntime.Cache[rootRelativePath] as string;
}
}
Then you can reference the bundled file like so...
#section scripts {
<script src="#StaticFile.Version("~/Scripts/app/myAppBundle.min.js")"></script>}
Then you have control of the token and can do what you want with it.

C# Get form names of project A from Project B

I have two Projects in one solution, project A and project B (using VS2010 Ultimate and C# windows application).
Project B acts as a user management application for project A.
In project B i have a form that contains a chekcedlistbox control that will list all project A forms names and texts (this form will let system administrator to grant users the forms that are allowed to view/edit based on their security groups)
this is my code:
private void GetFormNames()
{
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (Type t in a.GetTypes())
{
if (t.BaseType == typeof(Form))
{
var emptyCtor = t.GetConstructor(Type.EmptyTypes);
if (emptyCtor != null)
{
var f = (Form)emptyCtor.Invoke(new object[] { });
string FormText = f.Text;
string FormName = f.Name;
checkedListBox1.Items.Add("" + FormText + "//" + FormName + "");
}
}
}
}
}
the result i am getting is the form names of my current project (B) and Empty lines(//) and Select Window//MdiWindowDialog, PrintPreview.
I'm going to assume you've referenced ProjectA correctly and all the forms you're interested in actually have a public parameterless constructor. The problem is likely caused by ProjectA not being loaded yet, you can fix this in multiple ways. Probably the most direct is to use the static Assembly.Load (as long as the files are in the same directory, if not it gets more complicated).
try
{
Assembly projectA = Assembly.Load("ProjectA"); // replace with actual ProjectA name
// despite all Microsoft's dire warnings about loading from a simple name,
// you should be fine here as long as you don't have multiple versions of ProjectA
// floating around
foreach (Type t in projectA.GetTypes())
{
if (t.BaseType == typeof(Form))
{
var emptyCtor = t.GetConstructor(Type.EmptyTypes);
if (emptyCtor != null)
{
var f = (Form)emptyCtor.Invoke(new object[] { });
// t.FullName will help distinguish the unwanted entries and
// possibly later ignore them
string formItem = t.FullName + " // " + f.Text + " // " + f.Name;
checkedListBox1.Items.Add(formItem);
}
}
}
}
catch(Exception err)
{
// log exception
}
Another (probably cleaner solution) would be to have all the forms you're interested in inherit from a single base form. You could then load the assembly from that known Type and check that each enumerated Type inherits from it before adding it to your list. This is a more extensive change, however, and touches ProjectA.
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly a in assemblies)
{
Type[] types = a.GetTypes();
foreach (Type t in types)
{
if (t.BaseType == typeof(Form))
{
//Do Your works
}
}
}
Try This Code:
private void GetFormNames()
{
Type[] AllTypesInProjects = Assembly.GetExecutingAssembly().GetTypes();
for (int i = 0; i < AllTypesInProjects.Length; i++)
{
if (AllTypesInProjects[i].BaseType == typeof(Form))
{ /* Convert Type to Object */
Form f = (Form)Activator.CreateInstance(AllTypesInProjects[i]);
string FormText = f.Text;
listBox1.Items.Add(FormText);
}
}
}

Categories

Resources