基于跨项目的代码文件进行构建时代码验证和生成

发布于 2024-12-28 09:35:55 字数 968 浏览 6 评论 0原文

我正在寻找一种方法,让我可以使用 Visual Studio 2010(非 Express)和 MSBuild 在构建过程中验证代码和生成器代码。

后台验证:

我正在使用 WCF Web Api 编写 RESTful Web 服务。在代表 Web 服务的服务类中,我必须定义一个端点,另外将参数声明为普通测试。当端点声明中的参数名称与 C# 方法的参数不同时,我会收到错误 - 不幸的是,在访问 Web 服务的运行时,而不是在编译时。因此,我认为最好将 Web 服务类作为编译步骤的一部分来分析这样的缺陷,当出现问题时返回错误。

示例:

[WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
public string MyMethod(string param1, string parameter2) {
    // Accessing the web service now will result in an error,
    // as there's no fitting method-parameter named "param2".
}

我还想强制一些命名规则,例如 GET 方法必须以“Get”一词开头。我相信这将有助于该服务在与多个同事合作时保持更高的可维护性。

后台生成:

我将在其他几个项目中使用此 REST Web 服务,因为我需要编写一个客户端来访问此服务。但我不想为每一个都编写一个客户端,每当服务发生变化时总是进行调整。我希望根据 Web 服务代码文件自动生成客户端。

以前的方法:

到目前为止,我尝试使用使用DTE接口的T4模板来解析代码文件并验证它,或者生成客户端。手动保存时,这在 Visual Studio 中工作得很好,但在构建过程中集成它却不太有效,因为 Visual Studio 主机无法使用 MSBuild。

欢迎任何建议。 :)

I'm looking for a method that let's me validate code and generator code as part of the build process, using Visual Studio 2010 (not express) and MSBuild.

Background Validation:

I'm writing a RESTful web service using the WCF Web Api. Inside the service class that represents the web service I have to define an endpoint, declaring additionally parameters as plain test. When the parameter name inside the endpoint declaration differs from the parameter of the C# method I get a error - unfortunately at run time when accessing the web service, not at compile time. So I thought it would be nice to analyze the web service class as part of the compile step for flaws like this, returning an error when something is not right.

Example:

[WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
public string MyMethod(string param1, string parameter2) {
    // Accessing the web service now will result in an error,
    // as there's no fitting method-parameter named "param2".
}

Also I'd like to enforce some naming rules, such as GET-Methods must start with the "Get" word. I believe this will help the service to remain much more maintainable when working with several colleagues.

Background Generation:

I will be using this REST web service in a few other projects, there for I need to write a client to access this service. But I don't want to write a client for each of these, always adjusting whenever the service changes. I'd like the clients to be generated automatically, based upon the web service code files.

Previous approach:

So far I tried to use a T4 template using the DTE interface to parse the code file and validate it, or generate the client. This worked fine in Visual Studio when saving manually, but integrating this in the build process turned out to be not so working well, as the Visual Studio host is not available using MSBuild.

Any suggestion is welcome. :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

抱猫软卧 2025-01-04 09:35:55

您可以使用反射(仅反射上下文)在编译后检查程序集,而不是使用 DTE 或其他方法来解析 C# 代码。使用反射是一种更强大的解决方案,而且可能也更快(特别是如果您使用 Mono.Cecil 进行反射)。

对于 MSBuild 集成,我建议编写一个 自定义 MSBuild 任务 - 这是相当的比编写由 MSBuild 执行的命令行实用程序更简单、更健壮/优雅。

Instead of using DTE or some other means to parse the C# code you could use reflection (with Reflection-Only context) to examine the assembly after it's compiled. Using reflection is a more robust solution and probably faster also (especially if you use Mono.Cecil to do the reflecting).

For the MSBuild integration I would recommend writing a custom MSBuild task - it's fairly easy and more robust/elegant than writing a command line utility that's executed by MSBuild.

风轻花落早 2025-01-04 09:35:55

这可能是一个远景,但仍然符合“任何建议”:)

您可以编译代码,然后运行构建后命令,这将是您必须编写的工具,它使用反射来比较解析的 UriTemplate 文本使用方法参数名称,捕获错误并以 MSBuild 拾取的方式输出它们。查看 此链接了解有关如何输出的信息,以便 MSBuild 将错误放入 Visual Studio 错误列表中。如果发现错误,构建后工具可以删除已编译的程序集,从而“模拟”失败的构建。

这是 SO 链接 也引导我访问 MSBuild 博客,仅供参考。

华泰

This may be a long shot but still qualifies as "any suggestion" :)

You could compile the code, then run a post-build command which would be a tool that you'd have to write which uses reflection to compare the parsed UriTemplate text with the method parameter names, catching errors and outputting them in a manner that MSBuild will pickup. Look at This Link for information on how to output so MSBuild will put the errors in the visual studio error list. The post-build tool could then delete the compiled assemblies if errors were found, thus "simulating" a failed build.

Here's the SO Link that lead me to the MSBuild Blog too, just for reference.

HTH

浪漫人生路 2025-01-04 09:35:55

对于执行方面,自定义 FxCop 规则可能非常适合。

对于客户端代码生成,有很多可能性。如果您喜欢 T4 方法,可能有一种方法可以让它与 MSBuild 一起工作(但您肯定需要提供有关现在不起作用的更多详细信息)。如果您无论如何都想要替代方案,基于反射的构建后工具是另一种选择......

For the enforcement side of things, custom FxCop rules would probably be a very good fit.

For the client code generation, there are quite a few possibilities. If you like the T4 approach, there is probably a way to get it working with MSBuild (but you would definitely need to provide a bit more detail regarding what isn't working now). If you're want an alternative anyway, a reflection-based post-build tool is yet another way to go...

风铃鹿 2025-01-04 09:35:55

这是一个简短的、极其难看的程序,您可以在一个程序集或一组程序集上运行该程序(只需将 dll 作为参数传递)以执行 WebGet UriTemplate 检查。如果您没有通过任何内容,它会自行运行(并且会适当地失败,因为它是它自己的单元测试)。

该程序将向标准输出打印缺少参数的方法的名称以及缺少参数的名称,如果找到任何参数,将返回非零返回代码(程序失败的标准),使其适合作为构建后事件。如果您的眼睛流血,我不承担任何责任:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.ServiceModel.Web;

namespace ConsoleApplication1
{
    class Program
    {
        static int Main(string[] args)
        {
            var failList = new ConcurrentDictionary<MethodInfo, ISet<String>>();
            var assembliesToRunOn = (args.Length == 0 ? new[] {Assembly.GetExecutingAssembly()} : args.Select(Assembly.LoadFrom)).ToList();
            assembliesToRunOn.AsParallel().ForAll(
                a => Array.ForEach(a.GetTypes(), t => Array.ForEach(t.GetMethods(BindingFlags.Public | BindingFlags.Instance),
                    mi =>
                        {
                            var miParams = mi.GetParameters();
                            var attribs = mi.GetCustomAttributes(typeof (WebGetAttribute), true);
                            if (attribs.Length <= 0) return;
                            var wga = (WebGetAttribute)attribs[0];
                            wga.UriTemplate
                                .Split('/')
                                .ToList()
                                .ForEach(tp =>
                                             {
                                                 if (tp.StartsWith("{") && tp.EndsWith("}"))
                                                 {
                                                     var tpName = tp.Substring(1, tp.Length - 2);
                                                     if (!miParams.Any(pi => pi.Name == tpName))
                                                     {
                                                         failList.AddOrUpdate(mi, new HashSet<string> {tpName}, (miv, l) =>
                                                                                                                    {
                                                                                                                        l.Add(tpName);
                                                                                                                        return l;
                                                                                                                    });
                                                     }
                                                 }
                                             });
                        })));
            if (failList.Count == 0) return 0;
            failList.ToList().ForEach(kvp => Console.Out.WriteLine("Method " + kvp.Key + " in type " + kvp.Key.DeclaringType + " is missing the following expected parameters: " + String.Join(", ", kvp.Value.ToArray())));
            return failList.Count;
        }

        [WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
        public void WillPass(String param1, String param2) { }

        [WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
        public void WillFail() { }

        [WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
        public void WillFail2(String param1) { }
    }
}

Here is a short, extremely ugly program that you can run over an assembly or group of assemblies (just pass the dlls as arguments) to perform the WebGet UriTemplate check. If you don't pass anything, it runs on itself (and fails, appropriately, as it is its own unit test).

The program will print out to stdout the name of the methods that are missing the parameters and the names of the missing parameters, and if any are found, will return a non-zero return code (standard for a program failing), making it suitable as a post-build event. I am not responsible if your eyes bleed:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.ServiceModel.Web;

namespace ConsoleApplication1
{
    class Program
    {
        static int Main(string[] args)
        {
            var failList = new ConcurrentDictionary<MethodInfo, ISet<String>>();
            var assembliesToRunOn = (args.Length == 0 ? new[] {Assembly.GetExecutingAssembly()} : args.Select(Assembly.LoadFrom)).ToList();
            assembliesToRunOn.AsParallel().ForAll(
                a => Array.ForEach(a.GetTypes(), t => Array.ForEach(t.GetMethods(BindingFlags.Public | BindingFlags.Instance),
                    mi =>
                        {
                            var miParams = mi.GetParameters();
                            var attribs = mi.GetCustomAttributes(typeof (WebGetAttribute), true);
                            if (attribs.Length <= 0) return;
                            var wga = (WebGetAttribute)attribs[0];
                            wga.UriTemplate
                                .Split('/')
                                .ToList()
                                .ForEach(tp =>
                                             {
                                                 if (tp.StartsWith("{") && tp.EndsWith("}"))
                                                 {
                                                     var tpName = tp.Substring(1, tp.Length - 2);
                                                     if (!miParams.Any(pi => pi.Name == tpName))
                                                     {
                                                         failList.AddOrUpdate(mi, new HashSet<string> {tpName}, (miv, l) =>
                                                                                                                    {
                                                                                                                        l.Add(tpName);
                                                                                                                        return l;
                                                                                                                    });
                                                     }
                                                 }
                                             });
                        })));
            if (failList.Count == 0) return 0;
            failList.ToList().ForEach(kvp => Console.Out.WriteLine("Method " + kvp.Key + " in type " + kvp.Key.DeclaringType + " is missing the following expected parameters: " + String.Join(", ", kvp.Value.ToArray())));
            return failList.Count;
        }

        [WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
        public void WillPass(String param1, String param2) { }

        [WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
        public void WillFail() { }

        [WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
        public void WillFail2(String param1) { }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文