Мой COM-класс, расположенный в сборке .NET, foo.dll создает ADODB.Recordset
из .NET IDataReader
, а затем возвращает его вызывающей стороне, в моем случае классической устаревшей странице ASP.Однако мы определили узкое место в производительности, которое, похоже, является вызовом new ADODB.Recordset()
, и выяснили, что установка Embed interop types
в adodb.dll значительно повышает производительность.К сожалению, это вызывает исключение в классическом ASP:
System.TypeLoadException: Could not load type 'ADODB.FieldsToInternalFieldsMarshaler' from assembly 'foo ...'.
at System.StubHelpers.StubHelpers.CreateCustomMarshalerHelper(IntPtr pMD, Int32 paramToken, IntPtr hndManagedType)
at ADODB._Recordset.get_Fields()
На разных страницах написано, что исправление для этой проблемы - НЕ встраивать типы взаимодействия из adodb.dll, но производительность затем идет на юг ... Улучшения производительности были провереныс помощью другого приложения .NET, ссылающегося на foo.dll со встроенным adodb.dll, а не встроенным.Кроме того, почему наши тестовые приложения .NET могут работать с foo.dll со встроенными типами взаимодействия, а классический ASP - нет?Спасибо за любые советы.
РЕДАКТИРОВАТЬ:
Проблема производительности была отслежена до выхода компилятора при внедрении, а не встраивания типов взаимодействия из adodb.dll.Repro:
- Создать новую библиотеку c #,
- Добавить ссылку на adodb.dll
- Пометить ее как
Embed interop types
false Добавьте код и скомпилируйте:
using System;
namespace cax_test
{
public class Class1
{
public static ADODB.Recordset Get()
{
return new ADODB.Recordset();
}
}
}
Откройте полученный dll, например, в ILSpy, и обратите внимание, что конструктор ADODB.Recordset
был переписан в:
return new ADODB.RecordsetClass();
Измените Embed interop types
на true и перекомпилируйте
Снова откройте в ILSpy и обратите внимание, что конструктор:
return (ADODB.Recordset)Activator.CreateInstance(Marshal.GetTypeFromCLSID(new Guid("00000535-0000-0010-8000-00AA006D2EA4")));
При ссылкетакая сборка из консольного приложения и вызов метода Class1.Get()
1000x не имеет значения, если ссылаться на него из веб-приложения и делать то же самое, версия с RecordsetClass
будет в несколько раз медленнее версии с CreateInstance
.Пример такого приложения, которое будет размещено в IIS, можно загрузить с здесь , затем просто нажать http://localhost/test_app/t2.ashx и увидеть разницу между активатором и версией конструктора.Чтобы увидеть реализацию методов cax_test.dll, просто откройте его в ILSpy, это выглядит так:
using ADODB;
using System;
using System.Runtime.InteropServices;
namespace cax_test
{
public class Class1
{
public static Recordset GetViaActivator()
{
return (Recordset)Activator.CreateInstance(Marshal.GetTypeFromCLSID(new Guid("00000535-0000-0010-8000-00AA006D2EA4")));
}
public static Recordset GetViaConstructor()
{
return new RecordsetClass();
}
}
}
Для меня измеренные времена:
activator: 00:00:00.1475626
constructor: 00:00:01.3152554