【问题标题】:MsTest and OWIN, duplicated controllerMsTest 和 OWIN,复制控制器
【发布时间】:2015-10-20 15:20:07
【问题描述】:

我有一组由 MsTest 围绕 OWIN 执行的集成测试,使用 OWIN 启动方法进行自我主机。 测试非常简单,使用以下模式:

WebApp.Start<Startup>(url: appAddress);
HttpClient client = new HttpClient();
client.DefaultRequestHeaders
    .Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync(uri).Result;

当我在本地 DEV 机器上运行测试时,它们是绿色的。 当我们在 Build Machine 中从 Visual Studio 运行测试时,它们是绿色的。 如果他们从队列中运行,几天前,我们开始从 OWIN 收到这个烦人的错误:

    *** OWIN STARTED ***
    {"Message":"An error has occurred.",
"ExceptionMessage":"Multiple types were found that match the controller named 'xxx'. 
This can happen if the route that services this request ('odata/v1/{*odataPath}') found multiple controllers defined with the same name but differing namespaces, which is not supported.
The request for 'xxx' has found the following matching controllers:
namespace.V1.Controllers.xxxController
namespace.V1.Controllers.xxxController",
"ExceptionType":"System.InvalidOperationException","StackTrace":"   at System.Web.Http.Dispatcher.DefaultHttpControllerSelector.SelectController(HttpRequestMessage request)
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"}

这很奇怪,因为我们使用 Windsor 来解析控制器,并且我们还打印出 Windsor 中的注册信息,当然只有一个具有该名称的控制器。只有在 MsBuild 中执行代码时,我们才会在所有控制器上遇到这个问题……在 Build Machine 的 Visual Studio 中或在生产中,它可以工作。 可能是错误是其他东西,但被 OWIN 吞噬了?

【问题讨论】:

  • 你用的是哪个版本的owin?
  • 最新的应该是2.2 是一个奇怪的问题,因为配置在rebuild后已经自动整理出来了
  • 我在 owin 3 和 nunit 上遇到了同样的问题,但它只发生在其中一个测试项目中。
  • 在我的情况下,我们在 MsTest 中重新托管 OWIN 应用程序以避免出现问题,我可以在这里发布代码,稍等

标签: visual-studio mstest owin


【解决方案1】:

我遇到了同样的错误,就我而言,这是多种因素的结合:

我正在使用 Nunit 和 Owin 进行自托管。 在测试的设置中,我启动了 Web 服务器:

_server = WebApp.Start<Startup>(new StartOptions(baseAddress));

在测试的拆解中我杀死了它:

_server.Dispose();

我的一个测试是使用在设置之前执行的 TestCaseSource

[TestCaseSource(typeof(TestData), "TestDataSource")]
public void Test_With_Source(TestData testcase)

TestData.TestDataSource 内部,我正在加载包含控制器的程序集以获取一些路由信息:

loadedAssembly = Assembly.LoadFile(dll); //First load of the assembly

在 Web 服务器启动时(在设置中),程序集已经加载了一次(由 TestCaseSource),所以我最终得到了两次相同的程序集,并且 web api 抱怨同一个控制器的重复。

我的解决方法是通过以下方式删除 Assembly.LoadFile(dll):

typeof(BaseController).Assembly

这使得包含 BaseController 的程序集没有被加载两次。

【讨论】:

    【解决方案2】:

    我们通过在 IIS express 中托管 OWIN 项目解决了测试中的问题

    public class IisExpressRunner : IDisposable
    {
         /// <summary>
         ///   Stores whether this instance has been disposed.
         /// </summary>
         private Boolean _isDisposed;
    
         /// <summary>
         ///   Stores the IIS Express process.
         /// </summary>
         private Process _process;
    
         /// <summary>
         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
         /// </summary>
         public void Dispose()
         {
             Dispose(true);
             GC.SuppressFinalize(this);
         }
    
         /// <summary>
         /// Starts IIS Express using the specified directory path and port.
         /// </summary>
         /// <param name="configPath">
         /// The directory path.
         /// </param>
         /// <param name="siteName">
         /// The port.
         /// </param>
         public void Start(String configPath, string siteName)
         {
             String iisExpressPath = DetermineIisExpressPath();
             String arguments = String.Format(
            CultureInfo.InvariantCulture, "/config:\"{0}\" /site:{1}", configPath, siteName);
    
             ProcessStartInfo info = new ProcessStartInfo(iisExpressPath)
             {
                WindowStyle = ProcessWindowStyle.Hidden,
                ErrorDialog = true,
                LoadUserProfile = true,
                CreateNoWindow = false,
                UseShellExecute = false,
                Arguments = arguments
            };
    
            Thread startThread = new Thread(() => StartIisExpress(info))
            {
                IsBackground = true
            };
    
            startThread.Start();
         }
    
         /// <summary>
         /// Releases unmanaged and - optionally - managed resources.
         /// </summary>
         /// <param name="disposing">
         /// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.
         /// </param>
         protected virtual void Dispose(Boolean disposing)
         {
             if (_isDisposed)
             {
                 return;
             }
    
            if (disposing)
            {
                // Free managed resources
                if (_process.HasExited == false)
                {
                    _process.CloseMainWindow();
                }
    
                _process.Kill();
                _process = null;
            }
    
            // Free native resources if there are any
            _isDisposed = true;
        }
    
         /// <summary>
         /// Determines the IIS express path.
         /// </summary>
         /// <returns>
         /// A <see cref="String"/> instance.
         /// </returns>
         private static String DetermineIisExpressPath()
         {
            String iisExpressPath;
    
            if (Environment.Is64BitOperatingSystem)
            {
                iisExpressPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
            }
            else
            {
                iisExpressPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
            }
    
            iisExpressPath = Path.Combine(iisExpressPath, @"IIS Express\iisexpress.exe");
    
            return iisExpressPath;
        }
    
         /// <summary>
         /// Starts the IIS express.
         /// </summary>
         /// <param name="info">
         /// The info.
         /// </param>
         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
        Justification = "Required here to ensure that the instance is disposed.")]
         private void StartIisExpress(ProcessStartInfo info)
         {
            try
            {
                _process = Process.Start(info);
                _process.WaitForExit();
            }
            catch (Exception)
            {
                Dispose();
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 2014-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-18
      相关资源
      最近更新 更多