You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将OptionsProvider移至类库后VSIX项目编译失败:类型加载异常

问题描述

我正在开发一款管理GitHub Issues的Visual Studio扩展,受CommunityToolkit.MVVM特性限制,按照@Sergio0694的建议,我将ViewModel、服务等代码拆分到独立的VisGit.Core类库项目中,负责VS「工具>选项」的OptionsProvider也因依赖配置移至该类库。

主VSIX项目VisGit包含MainView工具窗口视图,通过XAML直接绑定VisGit.Core中的MainViewModel实例,无代码后置逻辑:

<UserControl x:Class="VisGit.Views.MainView"
        xmlns:viewmodels="clr-namespace:VisGit.Core.ViewModels;assembly=VisGit.Core"/>
    <UserControl.DataContext>
        <viewmodels:MainViewModel />
    </UserControl.DataContext>

VisGit项目已引用VisGit.Core,VisGit.Core已引用Community.VisualStudio.Toolkit.17。我在VisGit.Core中编写的OptionsProvider代码如下:

namespace VisGit.Core.Services
{
    public partial class OptionsProvider
    {
        // Register the options with this attribute on your package class:
        // [ProvideOptionPage(typeof(OptionsProvider.UserSettingsOptions), "VisGit.Services", "UserSettings", 0, 0, true, SupportsProfiles = true)]
        [ComVisible(true)]
        public class UserSettingsOptions : BaseOptionPage<UserSettings>
        { }
    }

    public class UserSettings : BaseOptionModel<UserSettings>
    {
        private string personalAccessToken = "{UNSET}";

        [DisplayName("Personal Access Token")]
        public string PersonalAccessToken
        {
            get => Encyption.DpapiToInsecureString(Encyption.DpapiDecryptString(personalAccessToken));
            set => personalAccessToken = Encyption.DpapiEncryptString(Encyption.DpapiToSecureString(value));
        }
    }
}

随后在VisGit项目的VisGitPackage.cs中配置ProvideOptionPage属性,指向类库中的UserSettingsOptions:

namespace VisGit
{
    [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
    [InstalledProductRegistration(Vsix.Name, Vsix.Description, Vsix.Version)]
    [ProvideToolWindow(typeof(MainToolWindow.Pane), Style = VsDockStyle.Tabbed, Window = WindowGuids.SolutionExplorer)]
    [ProvideMenuResource("Menus.ctmenu", 1)]
    [Guid(PackageGuids.VisGitString)]
    [ProvideOptionPage(typeof(VisGit.Core.Services.OptionsProvider.UserSettingsOptions), "VisGit.Core.Services", "UserSettings", 0, 0, true, SupportsProfiles = true)]
    public sealed class VisGitPackage : ToolkitPackage
    {
        protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
        {
            await this.RegisterCommandsAsync();

            this.RegisterToolWindows();
        }
    }
}

目前VisGit.Core可正常编译,但VisGit项目编译失败,报错如下:

Error       CreatePkgDef : error : TypeLoadException: Could not load type 'VisGit.Core.Services.OptionsProvider+UserSettingsOptions' from assembly 'VisGit.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
   at System.Reflection.CustomAttribute._CreateCaObject(RuntimeModule pModule, IRuntimeMethodInfo pCtor, Byte** ppBlob, Byte* pEndBlob, Int32* pcNamedArgs)
   at System.Reflection.CustomAttribute.CreateCaObject(RuntimeModule module, IRuntimeMethodInfo ctor, IntPtr& blob, IntPtr blobEnd, Int32& namedArgs)
   at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType, Boolean mustBeInheritable, IList derivedAttributes, Boolean isDecoratedTargetSecurityTransparent)
   at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit)
   at Microsoft.VisualStudio.Tools.CreatePkgDef.ProcessAssembly(String fileName, Hive hive, PkgDefContext context, Boolean register, RegistrationMode mode) in D:\a\_work\1\s\src\product\vssdk\tools\CreatePkgDef\CreatePkgDef.cs:line 341
   at Microsoft.VisualStudio.Tools.CreatePkgDef.DoCreatePkgDef(InputArguments inputArguments) in D:\a\_work\1\s\src\product\vssdk\tools\CreatePkgDef\CreatePkgDef.cs:line 202
   at Microsoft.VisualStudio.Tools.CreatePkgDef.Main(String[] arguments) in D:\a\_work\1\s\src\product\vssdk\tools\CreatePkgDef\CreatePkgDef.cs:line 91 VisGit      

注意到OptionsProvider是partial类,但无其他分部定义。当前架构下,ViewModel通过XAML实例化,无法在VSIX项目中实例化UserSettings并传入ViewModel,且希望ViewModel能独立获取配置,寻求该编译异常的解决方法。


解决方法

1. 修复Partial类定义问题

OptionsProvider标记为partial但无其他分部实现,会导致类型加载异常。直接移除partial修饰符,改为普通类:

public class OptionsProvider
{
    [ComVisible(true)]
    public class UserSettingsOptions : BaseOptionPage<UserSettings>
    { }
}

2. 确保类库引用的正确性

  • 检查VisGit项目对VisGit.Core的引用为项目引用,而非文件引用,避免编译时无法定位最新程序集。
  • 统一两个项目的输出目录,在项目属性「生成」选项中设置相同的输出路径,确保依赖程序集可被正确找到。

3. 调整OptionsProvider的可见性与COM注册

  • 确认VisGit.Core项目「生成」属性中勾选了「使程序集COM可见」,或手动在AssemblyInfo.cs添加[assembly: ComVisible(true)],保证UserSettingsOptions的[ComVisible(true)]属性生效。
  • 简化OptionsProvider的命名空间,例如改为VisGit.Core.Options,避免嵌套过深导致类型解析失败。

4. 实现ViewModel独立获取配置的方案

由于ViewModel通过XAML实例化无法直接注入配置,可在VisGit.Core中添加静态配置服务,借助Toolkit API获取配置:

namespace VisGit.Core.Services
{
    public static class ConfigurationService
    {
        public static UserSettings GetUserSettings()
        {
            return VS.Services.GetOptionsAsync<UserSettings>().GetAwaiter().GetResult();
        }
    }
}

在MainViewModel中调用该服务:

public class MainViewModel : ObservableObject
{
    private readonly UserSettings _settings;

    public MainViewModel()
    {
        _settings = ConfigurationService.GetUserSettings();
        // 后续直接使用_settings中的配置项
    }
}

5. 清理缓存并重新生成

  • 删除解决方案下所有bin和obj目录,清除编译缓存。
  • 先编译VisGit.Core项目,再编译VisGit项目,确保依赖程序集为最新版本。

内容的提问来源于stack exchange,提问作者stigzler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 05:17:34