【问题标题】:How to add translations to database after API response with EF Core?如何在使用 EF Core 进行 API 响应后向数据库添加翻译?
【发布时间】:2021-01-12 18:54:12
【问题描述】:

我需要添加一个项目并使用外部 API 翻译服务将其描述翻译成多种语言(因此它们存在于数据库中,以后可以获取不同语言的项目)。由于翻译需要相当长的时间 - 我首先需要返回 API 响应,然后翻译描述并将其他语言环境行添加到数据库中。
数据库架构如下所示:

数据库上下文设置:

public class ProjectsContext : DbContext
{
    public ProjectsContext(DbContextOptions<ProjectsContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder mb)
    {
        mb.Entity<Project>().ToTable("Project");
        mb.Entity<ProjectLocale>().ToTable("ProjectLocales")
            .HasKey(pl => new {pl.ProjectId, pl.Locale});
    }
    public DbSet<Project> Projects { get; set; }
}

Startup 类中,ConfigureServices 方法:

services.AddDbContext<ProjectsContext>(options =>
    options.UseMySql(envConfig.PROJECTS_DB_CONNECTION_STRING_MASTER));

模型:

public class Project
{
    public int Id { get; private set; }
    public List<ProjectLocale> ProjectLocales { get; private set; } = new List<ProjectLocale>();
    public async Task TranslateDescription(Translator translator, LanguageEnum currentLanguage)
    {
        ProjectLocales = await translator.Translate(ProjectLocales, currentLanguage);
    }
}
public class ProjectLocale
{
    public int ProjectId { get; set; }
    public string Locale { get; set; }
    public string Description { get; set; }
}

在存储库中,我有以下 AddAddProjectDescriptionTranslation 方法:

public void Add(Project project)
{
    projectsContext.Projects.Add(project);
    projectsContext.SaveChanges();
    AddProjectDescriptionTranslations(project);
}

private async Task AddProjectDescriptionTranslations(Project project)
{
    await project.TranslateDescription(translator, headers.LanguageEnum);
    projectsContext.Projects.Update(project);
    projectsContext.SaveChanges();
}

我在控制器的 POST 方法中使用了Add 方法,下一行已经返回了添加的项目以及当前语言的描述。翻译器和标头被注入到我的存储库类中。
当前的解决方案给了我一个错误(只有在我使用断点执行异步方法时才能查看):

Cannot access a disposed object. A common cause of this error is disposing a context that was
resolved from dependency injection and then later trying to use the same context instance
elsewhere in your application. This may occur if you are calling Dispose() on the context,
or wrapping the context in a using statement. If you are using dependency injection, you should
let the dependency injection container take care of disposing context instances.
Object name: 'ProjectsContext'.

这个问题有什么解决办法吗?或者也许是实现相同目标的更好方法?在以当前语言向用户提供响应后,我需要将翻译保存在数据库中(我从前端收到当前语言项目的描述)以优化响应时间。

提前感谢您的宝贵时间!

【问题讨论】:

  • 您将上下文添加为Scoped,在您返回结果后将被丢弃。我的建议是使用Queue 服务/后台工作进程和IHostedService / .Net Channel。如果您正在使用微服务并希望实现无状态,那么使用队列将是一个更好的选择,如果您确定始终使用单个实例,那么可能可以更简单地选择使用 IHostedService 或任何插件(如 Hangfire)作为后台任务。或者您可以查看 .Net 频道。我还没有亲自玩过它,我认为它类似于队列服务。

标签: c# asp.net-core async-await asp.net-core-3.1 ef-core-3.1


【解决方案1】:

很难说你想要实现什么以及你是如何做到的,所以这个答案可能没用。我不确定你是如何处理上下文的,但你必须确保在所有调用完成后处理它。

最直接的方法

public async Task Add(Project project)
{
    using (var projectsContext= new ProjectsContext())
    {     
        // Perform data access using the context
    
        projectsContext.Projects.Add(project);
        projectsContext.SaveChanges();
        await AddProjectDescriptionTranslations(project, projectsContext);
     }
   }
}

private async Task AddProjectDescriptionTranslations(Project project, ProjectsContext projectsContext)
{
    await project.TranslateDescription(translator, headers.LanguageEnum);
    projectsContext.Projects.Update(project);
    projectsContext.SaveChanges();
}

【讨论】:

  • 嗨 :) 数据库上下文的生命(据我所知)由 IoC 容器管理。您的示例的问题是我们等待执行 translate 方法,因此阻塞线程并且在所有翻译准备好并插入数据库之前不返回 API 响应。执行时间太长,让人等待它完成。因此,我尝试实施的解决方案是以当前语言插入项目信息,然后启动另一个线程(因为 AddProjectDescriptionTranslations 是异步的,并且没有等待)进行翻译并插入到数据库中
  • 嗯,你一开始就没有正确地做,你不能调用异步方法而不像 AddProjectDescriptionTranslations(project);它可能有几个问题它永远不会执行,或者它会在 contxt 已经处理时尝试执行
  • 确实,上下文已被处理,这是我得到的例外。也许您对如何实现这一目标有所了解?在返回 API 响应后需要继续使用上下文的情况的一些最佳实践?
【解决方案2】:

所以,不确定这是否是最好的方法,但我能够通过以下方式解决它:
ProjectContextOnConfiguring 方法添加了另一个构造函数:

public ProjectsContext(string connectionString)
{
    this.connectionString = connectionString;
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    if (connectionString != null)
    {
        optionsBuilder.UseMySql(connectionString);
    }
}

添加ProjectContextFactory

public class ProjectsContextFactory
{
    private readonly EnvConfig envConfig;

    public ProjectsContextFactory(IOptions<EnvConfig> envConfig)
    {
        this.envConfig = envConfig.Value;
    }

    public ProjectsContext CreateProjectsContext()
    {
        return new ProjectsContext(envConfig.PROJECTS_DB_CONNECTION_STRING_MASTER);
    }
}

对 Add 和 AddTranslations 方法进行了以下更改:

public void Add(Project project)
{
    projectsContext.Projects.Add(project);
    projectsContext.SaveChanges();
    AddProjectDescriptionTranslations(project);
}

private async Task AddProjectDescriptionTranslations(Project project)
{
    using (var context = projectsContextFactory.CreateProjectsContext())
    {
        context.Attach(project);
        await project.TranslateDescription(translator, headers.LanguageEnum);
        context.Projects.Update(project);
        context.SaveChanges();
    }
}

它允许我使用当前语言描述保存项目,并在描述被翻译成其他语言并保存到数据库之前返回 API 响应。

【讨论】:

    猜你喜欢
    • 2017-04-23
    • 2019-12-04
    • 1970-01-01
    • 1970-01-01
    • 2013-11-19
    • 1970-01-01
    • 2017-12-27
    • 2018-08-30
    • 1970-01-01
    相关资源
    最近更新 更多