我有一个使用WebAPI2
的相当复杂的Owin
项目和使用Unity
的依赖注入。我现在有一个控制器操作从MultipartContent
获取Request.Content
,然后移交给服务(由DI创建),该服务将此文件临时存储到服务器的硬盘驱动器。这是必需的,因为该服务使用外部和本机DLL来读取此文件以进行某些验证和转换。因此,外部DLL还将转换后的文件以及转换结果(作为XML)存储在同一位置。我的服务现在读取转换结果并返回该结果。之后,临时文件将被删除。
现在的问题是我自己Dispose()
的{{1}}方法在该操作期间被调用两次,这实际上会破坏东西,以便下一个请求也失败。例如,我确实得到了无法创建控制器的错误,因为它没有无参数构造函数或IDependencyResolver
上的ObjectDisposedException
。之后DI容器正确重建,一切正常。等等。所以每一个请求都会失败。
所以这里有一些代码:
Owin Startup
System.Web.Http.HttpServer
嗯,我不认为这有什么特别之处。如您所见,由于[assembly: OwinStartup(typeof(MyNamespace.Startup))]
namespace myNamespace
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration httpConfiguration = new HttpConfiguration();
httpConfiguration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
app.UseCors(CorsOptions.AllowAll);
IUnityContainer container = new UnityContainer();
// setup dependency injection
container.RegisterType<IFileConversionService, FileConversionService>();
UnityConfig.RegisterControllers(container);
// register routes
WebApiConfig.Register(httpConfiguration);
// setup dependency resolver
httpConfiguration.DependencyResolver = new MyNamespace.UnityResolver(container);
app.UseWebApi(httpConfiguration);
}
}
}
,我不使用GlobalConfiguration.Configuration.DependencyResolver
。
我的DependencyResolver:
Owin
这一点没有什么特别之处,因为它只是从网络上的众多例子之一中复制而来。
现在是ControllerAction:
public class UnityResolver : IDependencyResolver
{
private IUnityContainer _container;
private bool _disposed;
public UnityResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
_container = container;
}
public object GetService(Type serviceType)
{
try
{
return _container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return _container.ResolveAll(serviceType);
}
catch (ResolutionFailedException)
{
return new List<object>();
}
}
public IDependencyScope BeginScope()
{
var child = _container.CreateChildContainer();
return new UnityResolver(child);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
_container.Dispose();
}
_disposed = true;
}
}
现在的服务:
public async Task<IHttpActionResult> Upload()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
MultipartMemoryStreamProvider provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
IList<MyFile> uploadFiles = new List<MyFile>();
foreach (var file in provider.Contents)
{
MyFile myFile = new MyFile
{
FileName = file.Headers.ContentDisposition.FileName.Trim('\"'),
Data = await file.ReadAsByteArrayAsync()
};
uploadFiles.Add(myFile);
}
List<ConversionResult> result = new List<ConversionResult>();
foreach(MyFile file in uploadFiles)
{
result.AddRange(_fileConversionService.ImportFile(file));
}
if (result!= null && result.Count() == uploadFiles.Count)
{
return Ok(importedParts);
}
return BadRequest("One or more files could not be imported.");
}
在public IEnumerable<ConversionResult> ImportFile(MyFile myFile)
{
lock (_lock)
{
IEnumerable<ConversionResult> conversionResult = new List<ConversionResult>();
try
{
string inputFile = SaveFileTemporarily(myFile);
string inputFileExtension = Path.GetExtension(inputFile);
string logFile = inputFile.Replace(inputFileExtension, ".txt");
// check if the file can be validated/converted by the DLL
if (ValidateFileExtension(inputFileExtension))
{
// call external DLL that writes log file and converted file
ConvertFile(inputFile, logFile, inputFileExtension);
conversionResult = ParseLogFile(logFile);
if (!conversionResult.Any())
{
// no errors found
conversionResult.Add(ConversionResult.NoError);
}
}
else
{
conversionResult.Add(ConversionResult.UnknownFileType);
}
}
finally
{
Directory.Delete(GenerateTempSavePath(myFile), true);
}
return conversionResult;
}
}
中,文件存储到某个文件夹,实际上是bin文件夹的子文件夹;例如SaveFileTemporarily()
和Directory.CreateDirectory(savePath);
通过调试我发现我总是在File.WriteAllBytes(fullPath, data);
函数中断两次,这与任何其他控制器操作不同。通过评论服务方法IDependencyResolver.Dispose()
中的内容,我发现这必须对文件操作做些什么。所以当我从服务类中完全删除ImportFile()
命名空间时,我没有得到那种行为,一切似乎都没问题。
我现在真的迷失了,因为我现在已经好几天都在追捕这个虫子了。有谁知道问题是什么?
更新
在网站上使用API时,我经常(但并非总是)会收到此错误:System.IO
这导致No 'Access-Control-Allow-Origin' header is present on the requested resource.
也在Owin Startup(CORs
)中配置
这一切暗示整个Owin应用程序或多或少随机丢弃/崩溃。在那些错误之后,再次调用Owin Startup方法,这不应该发生。何时调用Owin Startup?
在Visual Studio中激活所有异常时(DEBUG - &gt; Exceptions),我什么都得不到......
更新和解决方案:
好吧,我不知道为什么,但问题解决了。在服务类的app.UseCors(CorsOptions.AllowAll);
方法中,调用ImportFile()
。此方法使用我在类的构造函数中搜索的路径:
SaveFileTemporarily()
现在问题就在于此。当我使用
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
_tempFilePath = Path.GetDirectoryName(path) + TempDirectory;
一切正常。
我想它必须要做阴影复制......