How to get the using statements for a class using EnvDTE? - c#

I am working on a T4 template which produces partial classes based on existing partial classes.
Sometimes the generated code will reference types being used from the existing (non-generated) codebase.
The generated code must either fully qualify these types, or mimic the using statements it finds in the non-generated code.
Mimicking using statements seems better since it will support cases where the type is being referenced from a [Attribute(typeof(Something))], where EnvDTE only returns the string literal "typeof(Something)".
So: how do I find these using statements? I'm using tangible T4's AutomationHelper, but still can't seem to find a solution :(

You can get the using statements by looking at the FileCodeModel.CodeElements for the ProjectItem.
Each ProjectItem has a FileCodeModel property. The FileCodeModel.CodeElements will contain a CodeImport for each using statement. Note that the FileCodeModel.CodeElements will contain other things not just CodeImportss o you will need to check the type returned or filter the unwanted types.
An example is shown below. Here I am using the NuGet's Package Manager Console and PowerShell.
$p = Get-Project
$fileCodeModel = $p.ProjectItems.Item("Class1.cs").FileCodeModel
$fileCodeModel.CodeElements | % { $_.Namespace }
The code above assumes there is a Class1.cs file in the root of the project. For each using statement it will print the full namespace. Note that in the above code it is trying to print the Namespace for each CodeElement and some of the elements will not have this property so you will need to restrict this so it only looks at CodeImport types. The above will work for the following class file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ClassLibrary1
{
public class Class1
{
}
}
If you have using statements between the namespace ClassLibrary1 and the public class Class1 part you will need to do more work and look at the CodeNamespace members since the CodeImports will not be available directly from the FileCodeModel.CodeElements, but hopefully the above code should point you in the right direction.

Related

Namespace collision of ISet: System.Collections.Generic and Iesi.Collections.Generic

When upgrading project from 3.5 to 4.0 I encountered the collision of ISet class that exists in both
namespace:
System.Collections.Generic
Iesi.Collections.Generic
I have those two classes in hundreds of files. Prior to 4.0 there was not ISet in System.Collections.Generic.
How would you solve this pain ... ? Should I really add to each file: Iesi.Collections.Generic to ISet?
or give an alias:
using IesiGeneric = Iesi.Collections.Generic;
and use like that: IesiGeneric.ISet but all these means I have to change all those files ....
Is there another option?
UPDATE
What about creating interface like this:
using System;
using System.Linq;
using System.Text;
using Iesi.Collections.Generic;
namespace NameSpace
{
public interface IesiSet<T> : ISet<T>
{
}
}
and change Iesi Iset's to: IesiSet?
You should be able to add an alias directly to the type:
using ISet = Iesi.Collections.Generic.ISet;
Which should mean you ONLY need to update the head of each file.
However, going forward, I expect overtime (particularly as personnel changes) this is going to become a maintainability gotcha as people assume this is the built in ISet. I would take the pain now and replace globally now.
[also
I encountered the collision of ISet class that exists in both namespace
System.Collections.Generic.ISet is an interface. If your version is a class I would consider taking the pain of renaming it now, as it looks like an interface name]

Enforcing full namespace with using statement in C#

I have been looking around for a while now to see how can I enforce my C# projects to have full namespace path.
For example actual if namespace for class X is Foo.Bar.Car.Dealer when doing Ctrl+. in visual studio it sometimes puts statement like using Car.Dealer; this specially becomes a problem with multiple projects solution. I have been looking around for StyleCop rule or something that might help me get this done.
Any help or ideas?
EDIT
The above statement holds true only if the using class falls under same namespace prefix. Here is complete code example:
File: X.cs
namespace Foo.Bar.Car.Dealer {
class X {}
}
File: UsingClass.cs
namespace Foo.Bar.Another.ClassPath {
using Car.Dealer;
class UsingClass {
private X _x;
}
}
The VS picked using Car.Dealer but I want to enforce using Foo.Bar.Car.Dealer
I do not know about versions prior to 2012, but from then on the icon that pops up for action upon coming across an unknown type offers both adding the namespace via using directive or to simply prefix the type being referenced by the full namespace.
If you do not want to add the namespace via using directive (which would look like using Foo.Bar.Car.Dealer;),
then in your example you simply need to reference your type X as Foo.Bar.Car.Dealer.X.
Example:
//assuming your X type is instantiable
Foo.Bar.Car.Dealer.X myX = new Foo.Bar.Car.Dealer.X();

namespaces as result of the entity framework generated code

I have two Reason classes:
1. One that generated by the edmx file and inherited by the object context.
2. One that I created as POCO object.
While I write my queries I need to write the full namespace of the Reason POCO class:
using System.Collections.Generic;
using System.Linq;
using MyProj.Domain.Business.EntitiesRepository.System.Calls;
namespace MyProj.Data.EF4.EntitiesRepository.System.Calls
{
public class ReasonRepository:
EFRepository<MyProj.Domain.Business.Entities.System.Calls.Reason>, IReasonRepository
{
public IList<MyProj.Domain.Business.Entities.System.Calls.Reason> GetReasonsList()
{
return GetQuery().ToList();
}
}
}
If I am not writing the full namespace the compiler consider Reason as the generated object and not as the POCO object I need.
Is there any way of preventing write the full namespace?..
You could use using aliases .. see the example 1 in http://msdn.microsoft.com/en-us/library/sf0df423(v=vs.80).aspx.
How you do it -
in your using directive do something like -
using POCOObjects = MyProj.Domain.Business.Entities.System.Calls
after that you just need to type POCOObjects.Reason
Unless your Reason class is in the MyProj.Data.EF4.EntitiesRepository.System.Calls namespace, I think you can just add using MyProj.Domain.Business.Entities.System.Calls
Otherwise you might want to check out this Q&A C#: Problem trying to resolve a class when two namespaces are similar. The workaround being that you use the global:: namespace alias. More on that here: http://msdn.microsoft.com/en-us/library/c3ay4x3d.aspx

Namespace problem

I'm working on an ASP.NET web application with .NET 3.5 and have run into the following problem:
I'm working with a class under the namespace X.Web.Controls.Core which references the class Utils in the namespace X.X2.components.util.
I get an error that Utils is already defined in the namespace X.Web.Controls.Utils
This should not be possible since I can't find anything referencing that namespace from the class I'm working on. Any ideas?
I can't really see that there should be a problem unless you have a using statement referencing it somewhere. Do take care that code in a namespace will implicitly "see" classes in the same namespace, even if they're defined elsewhere.
Anyways, you can solve your problem by changing the class name (for the current code file only):
using X2Utils = X.X2.components.util.Utils;
The class will be named X2Utils in your code. Alternatively you can make a shortcut for its namespace:
using X2util = X.X2.components.util;
Now you can refer to the class using X2util.Utils.
You're working in X.Web.Controls.Core which is a subnamespace of X.Web.Controls. That means the namespace Utils in X.Web.Controls is implicitly visible.
Solve using an alias as suggested by Blixt.

Namespaces and Using Directives

If I have a namespace like:
namespace MyApp.Providers
{
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
}
Does this mean that if I create other files and classes with the same namespace, the using statements are shared, and I don't need to include them again?
If yes, isn't this a bit of a management headache?
No, it's only good for the namespace section inside the file. Not for all files inside the namespace.
If you put the using statement outside the namespace, then it applies to the entire file regardless of namespace.
It will also search the Usings inside the namespace first, before going to the outer scope.
You need to specify the using directive for any classes that you want to reference without qualification in each file where you want to use them.
Reference:
The scope of a using directive is
limited to the file in which it
appears.
No, it doesn't, and so no, it isn't.
Consider the fact that outside the namespace declaration, you are in the global namespace. Do using statements in that region of the source file affect the global namespace in other source files?
No. You'll need to include the namespaces in every class except on partial classes.
One side note: you're doing a very good practice of putting the using statements inside the Namespace. That's very good syntax.
Keep up the good work.
The using statements are valid for the code file in which they appear, with a minor twist; if you put the using statements inside the namespace, they are limited to the scope of that namespace, but still only within the same code file.
Usings only apply to the current file. Whether they're inside or outside the namespace declaration makes only a small difference:
The lookup order for types is as follows:
start in the innermost namespace declaration
look in the current namespace
look in the usings of the current namespace
go up to the parent namespace declaration and repeat from step 2
As a result, this program will compile fine:
namespace MyProject.Main {
using System;
class Program {
public static void Main(string[] args) {
Console.WriteLine("Hello, World!");
}
}
}
// in another file:
namespace MyProject.Console {
class Test {}
}
But if you move the using System; to the top, then the compilation will fail (MyProject.Console.WriteLine doesn't exist).

Categories

Resources