【问题标题】:Blazor client hosted constructor dependency injection in server controllerBlazor 客户端在服务器控制器中托管构造函数依赖注入
【发布时间】:2019-12-16 13:52:02
【问题描述】:

我在 Blazor 客户端托管应用程序中执行 API 请求时收到 HTTP 500。如果我省略控制器参数,则请求成功,但是,一旦我在控制器内设置参数,我就会得到 HTTP 500。

我已经检查并测试了容器并注册了接口。只要我不在服务器控制器构造函数中提供参数,客户端就可以工作。

我需要做什么才能在 blazor 应用程序的服务器控制器中注入构造函数?

在撰写本文时,我正在使用 Visual Studio 2019 16.2 预览版 4 和 .NET Core 3 预览版。


Startup.cs

public class Startup
{
    private readonly IDependency _dependency;

    public Startup()
    {
        _dependency = new Dependency();
    }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().AddNewtonsoftJson();
        services.AddResponseCompression(opts =>
        {
            opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
                new[] { "application/octet-stream" });
        });

        _dependency
            .SetServiceCollection(services)
            .SetServiceProvider(services.BuildServiceProvider());
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseResponseCompression();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBlazorDebugging();
        }

        app.UseClientSideBlazorFiles<Client.Startup>();

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
            endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
        });

        _dependency
            .SetWebHostEnvironment(env)
            .ConfigureDependencies()
            .ConfigureDatabases();
    }
}

Dependency.cs

public class Dependency : IDependency
{
    private IServiceCollection _services;
    private IServiceProvider _provider;
    private IWebHostEnvironment _environment;

    public IDependency SetServiceCollection(IServiceCollection services)
    {
        if (_services != null)
            throw new AlreadyConfiguredException($"{nameof(services)} is already configured.");

        _services = services;
        return this;
    }

    public IDependency SetServiceProvider(IServiceProvider provider)
    {
        if (_provider != null)
            throw new AlreadyConfiguredException($"{nameof(provider)} is already configured.");

        _provider = provider;
        return this;
    }

    public IDependency SetWebHostEnvironment(IWebHostEnvironment environment)
    {
        if (_environment != null)
            throw new AlreadyConfiguredException($"{nameof(environment)} is already configured.");

        _environment = environment;
        return this;
    }

    public IDependency ConfigureDatabases()
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(_environment.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{_environment.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();

        var config = builder.Build();
        var database = config.GetConnectionString("MenuDatabase");

        MenuStartup.Configure(_services, database);
        OpeningTimeStartup.Configure(_services, database);

        return this;
    }

    public IDependency ConfigureDependencies()
    {
        // Bounded Contexts

        // Domain Models
        _services.AddTransient<IMenu, Menu.Menu>();

        // Infrastructure
        _services.AddTransient(typeof(IRepository<,>), typeof(Repository<,>));
        _services.AddTransient<IFactory, Factory>();
        _services.AddSingleton<ICompositeCache, MemoryCompositeCache>();
        _services.AddTransient<IDataContextFactory, DataContextFactory>();
        _services.AddTransient<IMenuRepository, MenuRepository>();

        return this;
    }
}

IDependency.cs

public interface IDependency
{
    IDependency SetServiceCollection(IServiceCollection services);
    IDependency SetServiceProvider(IServiceProvider buildServiceProvider);
    IDependency ConfigureDependencies();
    IDependency SetWebHostEnvironment(IWebHostEnvironment environment);
    IDependency ConfigureDatabases();
}

MenuDataController.cs

[Route("api/[controller]")]
public class MenuDataController : Controller
{
    private readonly IMenu _menu;

    public MenuDataController(IMenu menu)
    {
        _menu = menu;
    }

    [HttpGet("[action]")]
    public IEnumerable<SpiceViewModel> Spices()
    {
        return new Spice[] { };
    }
}

Spices.razor

@inject HttpClient Http

<div class="alert alert-secondary mt-4" role="alert">
    <span class="oi oi-pencil mr-2" aria-hidden="true"></span>
    <strong>@Title</strong>

    <span class="text-nowrap">
        Please take our
        <a target="_blank" class="font-weight-bold" href="https://go.microsoft.com/fwlink/?linkid=2093904">brief survey</a>
    </span>
    and tell us what you think.

    @if (spices == null)
    {
        <p><em>Loading...</em></p>
    }
    else
    {
        <div class="form-group">
            <label for="spicesSelect">Select list:</label>
            <select class="form-control" id="spicesSelect">
                <option>None</option>
                @foreach (var spice in spices)
                {
                    <option value="@spice.Id">@spice.Name</option>
                }
            </select>
        </div>
    }
</div>

@code {
    [Parameter] string Title { get; set; }

    SpiceViewModel[] spices;

    protected override async Task OnInitAsync()
    {
        spices = await Http.GetJsonAsync<SpiceViewModel[]>("api/MenuData/Spices");
    }
}

【问题讨论】:

  • 如果您能提供一个最小的可重现示例(stackoverflow.com/help/minimal-reproducible-example),那就太棒了
  • 我认为如果您编写了客户端托管的 Blazor 应用程序,这将很容易理解。如果我尝试注入 SampleDataController 它会爆炸。
  • 请显示代码...
  • 请提供请求的代码 - 我们看不到您的屏幕,也看不到您提供的参数或调用 API 的方式。这是帮助您的所有相关信息。
  • 我已经添加了代码。这是 Blazor 客户端托管应用的基本框架。我已使用 IServicesCollection 中的 IServiceProvider 激活 IMenu 作为测试。

标签: asp.net-core .net-core visual-studio-2019 blazor


【解决方案1】:

我戴上眼镜升级了我的眼睛,结果发现控制器中有多个构造函数。按照 Microsoft 的建议打开服务器日志记录有很大帮助。

  • 我向微软展示的示例有多个带有单个参数的构造函数。 DI 不知道该选择哪个。
  • DI 不会自动激活未在容器中注册的实例类型。

【讨论】:

  • 所以解决办法就是提升你的视力,对吧?但是,嘿,有什么问题吗?可以分享一下吗...
【解决方案2】:

对于像我这样搜索“blazor 控制器依赖注入”并发现这个问题的人,这就是我得到的。

在您完成新项目创建后,原始 Blazor 客户端托管项目模板会显示以下代码 (WeatherForecastController.cs)。

public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
    this.logger = logger;
}

为了使用您自己创建的数据库上下文(或任何其他依赖注入),您只需要修改原始构造函数而不是创建一个新的构造函数(只保留一个构造函数)。

public WeatherForecastController(ILogger<WeatherForecastController> logger, MyDbContext context)
{
    this._context = context;
    this.logger = logger;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-08
    • 2012-03-09
    • 1970-01-01
    • 2020-09-26
    • 2018-12-04
    • 1970-01-01
    相关资源
    最近更新 更多