阿不

潜水

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

在ASP.NET MVC2中,我们经常这样来写表单模板:

<% Html.BeginForm("Post"); %>
<%:Html.EditorFor(m=>m.Name) %>
<%:Html.EditorFor(m=>m.DisplayName) %>
<%:Html.EditorFor(m=>m.Culture) %>
<%:Html.EditorFor(m=>m.Theme) %>
<%:Html.EditorFor(m=>m.HostName) %>
<%:Html.EditorFor(m=>m.VirtualPath) %>
<%:Html.EditorFor(m=>m.Version) %>
<%:Html.EditorFor(m=>m.Mode) %>
<% Html.EndForm(); %>

由于DataAnnotations的存在,我们完全可以把字段的视图元数据全部都在Model中设计好,那么开发编辑视图就变得非常的简单,可能只是对应Model的字段,一行一行的去写Html.EditorFor ,这是一种重复而枯燥的工作。因此我想到,写一个VS扩展,在HTML编辑器的右键菜单中添加一个命令,点一下就可以根据当前的Model的类型来生成上面的代码。这种扩展,对应的VSX是add-in,如何来开发这个add-in并不是我这里要记录的重点。网上有好多介绍如何开发add-in,不过我是参考这篇文章

这篇文章要介绍的重点是,如何在add-in中,动态去获得Model的Type实例。我们知道,我们在程序运行时,可以很容易的去得到Type实例。但是在这里,我们是要在VS中,去得到项目的Type实例,这是一个比较麻烦的事情。不过,从我们所知道的VS IDE的强大的智能提示功能就可以知道,要做到这个并不是不可能的事情。通过查找资料,整理了如下的代码,可以得到项目的所有的类型实例,还有其它对象是我们做很多扩展所必须的用到的:

public static class VsHelper
{
    public static IVsHierarchy GetCurrentHierarchy(IServiceProvider provider)
    {
        DTE vs = (DTE)provider.GetService(typeof(DTE));
        if (vs == null) throw new InvalidOperationException("DTE not found.");

        return ToHierarchy(vs.SelectedItems.Item(1).ProjectItem.ContainingProject);
    }
    public static IVsHierarchy ToHierarchy(EnvDTE.Project project)
    {
        if (project == null) throw new ArgumentNullException("project");

        string projectGuid = null;

        // DTE does not expose the project GUID that exists at in the msbuild project file.
        // Cannot use MSBuild object model because it uses a static instance of the Engine, 
        // and using the Project will cause it to be unloaded from the engine when the 
        // GC collects the variable that we declare.
        using (XmlReader projectReader = XmlReader.Create(project.FileName))
        {
            projectReader.MoveToContent();
            object nodeName = projectReader.NameTable.Add("ProjectGuid");
            while (projectReader.Read())
            {
                if (Object.Equals(projectReader.LocalName, nodeName))
                {
                    projectGuid = projectReader.ReadInnerXml();
                    break;
                }
            }
        }

        Debug.Assert(!String.IsNullOrEmpty(projectGuid));

        IServiceProvider serviceProvider = new ServiceProvider(project.DTE as
            Microsoft.VisualStudio.OLE.Interop.IServiceProvider);

        return VsShellUtilities.GetHierarchy(serviceProvider, new Guid(projectGuid));
    }
    public static IVsProject3 ToVsProject(EnvDTE.Project project)
    {
        if (project == null) throw new ArgumentNullException("project");

        IVsProject3 vsProject = ToHierarchy(project) as IVsProject3;

        if (vsProject == null)
        {
            throw new ArgumentException("Project is not a VS project.");
        }

        return vsProject;
    }
    public static EnvDTE.Project ToDteProject(IVsHierarchy hierarchy)
    {
        if (hierarchy == null) throw new ArgumentNullException("hierarchy");

        object prjObject = null;
        if (hierarchy.GetProperty(0xfffffffe, -2027, out prjObject) >= 0)
        {
            return (EnvDTE.Project)prjObject;
        }
        else
        {
            throw new ArgumentException("Hierarchy is not a project.");
        }
    }
    public static EnvDTE.Project ToDteProject(IVsProject project)
    {
        if (project == null) throw new ArgumentNullException("project");

        return ToDteProject(project as IVsHierarchy);
    }

    public static IServiceProvider GetServiceProvider(EnvDTE.Project project)
    {
        IServiceProvider serviceProvider = new ServiceProvider(project.DTE as
            Microsoft.VisualStudio.OLE.Interop.IServiceProvider);

        //DynamicTypeService typeService = (DynamicTypeService)
        //    serviceProvider.GetService(typeof(DynamicTypeService));

        return serviceProvider;
    }
    private static ITypeResolutionService GetResolutionService(EnvDTE.Project project)
    {
        DynamicTypeService typeService = GetService<DynamicTypeService>(project);
        Debug.Assert(typeService != null, "No dynamic type service registered.");

        IVsSolution sln = GetService<IVsSolution>(project);
        IVsHierarchy hier;
        sln.GetProjectOfUniqueName(project.UniqueName, out hier);

        Debug.Assert(hier != null, "No active hierarchy is selected.");

        return typeService.GetTypeResolutionService(hier);
    }
    public static T GetService<T>(EnvDTE.Project project)
    {
        return (T)GetServiceProvider(project).GetService(typeof(T));
    }
    public static IDictionary<string, Type> GetAvailableTypes(IServiceProvider provider, bool includeReferences)
    {
        DynamicTypeService typeService = (DynamicTypeService)provider.GetService(typeof(DynamicTypeService));
        Debug.Assert(typeService != null, "No dynamic type service registered.");

        IVsHierarchy hier = VsHelper.GetCurrentHierarchy(provider);
        Debug.Assert(hier != null, "No active hierarchy is selected.");

        ITypeDiscoveryService discovery = typeService.GetTypeDiscoveryService(hier);
        Project dteProject = VsHelper.ToDteProject(hier);

        IDictionary<string, Type> availableTypes = new Dictionary<string, Type>();
        foreach (Type type in discovery.GetTypes(typeof(object), includeReferences))
        {
            // We will never allow non-public types selection, as it's terrible practice.
            if (type.IsPublic)
            {
                if (!availableTypes.ContainsKey(type.FullName))
                {
                    availableTypes.Add(type.FullName, type);
                }
            }
        }
        return availableTypes;
    }
    public static Type GetType(EnvDTE.Project project, string typeName)
    {
        var types = GetAvailableTypes(GetServiceProvider(project), true);
        if (types.ContainsKey(typeName))
        {
            return types[typeName];
        }
        return null;
    }
}

在Add-in的Connect实现中,我们可以通过如下的代码就可以根据类型名称得到它的类型实例:

private Project CurrentProject
{
    get
    {
        return _applicationObject.DTE.Solution.Projects.Item(_applicationObject.DTE.Solution.Projects.Count);
    }
}
public Type GetType(string typeName)
{
    Type modelType = VsHelper.GetType(CurrentProject, typeName);
    return modelType;
}

这里都是对COM 对象的操作,由于缺少资料文档的支持,开发起来相当的不方便。如有兴趣,参考以下几篇文章:

http://www.clariusconsulting.net/blogs/kzu/archive/2006/01/06/GetTypesFromProject.aspx

http://www.clariusconsulting.net/blogs/kzu/archive/2007/10/04/34019.aspx

http://www.clariusconsulting.net/blogs/kzu/archive/2006/01/06/GetTypesFromProject.aspx

http://msmvps.com/blogs/carlosq/archive/2008/03/06/how-do-i-get-a-system-type-from-a-type-name.aspx

http://weblogs.asp.net/cazzu/archive/2006/01/07/GetServiceFromDTE.aspx

最后也共享下我写的这个add-in:下载

posted on 2010-07-26 13:39  阿不  阅读(3885)  评论(5编辑  收藏  举报