Я пишу веб-приложение (asp.net core, mvc), и на данный момент весь мой код находится в контроллерах.Я хочу переместить некоторые из них в классы обслуживания, чтобы упростить повторное использование кода и немного привести в порядок мои классы контроллеров.
Проблема в том, что, когда я пытаюсь сделать это, я получаю ошибку'Невозможно получить доступ к удаленному объекту.'
Кажется, что во второй раз, когда я пытаюсь использовать класс доступа к базе данных (DBcontext или userManager), класс (или что-то еще) удаляется.
Пример моего кода ниже.Для краткости я удалил некоторые биты (удаленные биты в основном не имеют значения).
Во-первых, контроллер:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using MyProject.Data;
using MyProject.Models;
using Microsoft.AspNetCore.Identity;
using MyProject.Models.Api;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Configuration;
using MyProject.Services;
namespace MyProject.ApiControllers
{
[Produces("application/json")]
[Route("api/Migration")]
public class MigrationController : Controller
{
private readonly ApplicationDbContext _context;
private UserManager<ApplicationUser> _userManager;
private RoleManager<IdentityRole> _roleManager;
private IConfiguration _configuration;
private InitialMigrationService _initialMigrationService;
public MigrationController(ApplicationDbContext context, UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IConfiguration configuration)
{
_context = context;
_userManager = userManager;
_roleManager = roleManager;
_configuration = configuration;
_initialMigrationService = new InitialMigrationService(userManager, roleManager, context, configuration);
}
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpPost]
[Route("GetDumpData")]
public async Task<bool> GetDumpData([FromBody] ApiDataDumpInfo apiDataDumpInfo)
{
// I have removed some code here to download a file into dumpBytes (byte array). This works fine
Models.InitialMigration.DataDump dataDump = Models.InitialMigration.DataDump.DeserialiseFromByteArray(dumpBytes);
_initialMigrationService.MigrateDataDump(dataDump);
return true;
}
}
}
И класс обслуживания (и интерфейс):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MyProject.Data;
using MyProject.Models;
using MyProject.Models.InitialMigration;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
namespace MyProject.Services
{
public class InitialMigrationService : IInitialMigrationService
{
private UserManager<ApplicationUser> _userManager;
private RoleManager<IdentityRole> _roleManager;
private ApplicationDbContext _context;
private IConfiguration _configuration;
public InitialMigrationService(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, ApplicationDbContext context, IConfiguration configuration)
{
_context = context;
_userManager = userManager;
_roleManager = roleManager;
_configuration = configuration;
}
public bool MigrateDataDump(DataDump dump)
{
MigrateUserSetup(dump);
return true;
}
private void MigrateUserSetup(DataDump dump)
{
dump.UserSetupList.ForEach(u => u.Accounts = true);
dump.UserSetupList.ForEach(async delegate (DDUserSetup u)
{
if (string.IsNullOrEmpty(u.Email))
return;
var swUser = _context.SoftwareUser
.SingleOrDefault(du => du.OldID == u.ID);
if (swUser == null)
{
_context.SoftwareUser.Add(new Models.SoftwareUser
{
Name = u.Name
// Have left out lots of other fields being copied over
});
_context.SaveChanges();
swUser = _context.SoftwareUser
.SingleOrDefault(du => du.OldID == u.ID);
string userID = await EnsureUser(u.Password, u.Email, swUser.ID);
await EnsureRole(userID, ConstantData.ConstUserRole);
}
});
}
private async Task<string> EnsureUser(string testUserPw, string userName, int? SoftwareUserId)
{
var user = await _userManager.FindByNameAsync(userName);
IdentityResult result = null;
if (user == null)
{
user = new ApplicationUser { UserName = userName, SoftwareUserID = SoftwareUserId, Email = userName };
if (string.IsNullOrEmpty(testUserPw))
result = await _userManager.CreateAsync(user);
else
result = await _userManager.CreateAsync(user, testUserPw); // This is the line I get the error on.
}
return user.Id;
}
private async Task<IdentityResult> EnsureRole(string uid, string role)
{
try
{
IdentityResult IR = null;
if (!await _roleManager.RoleExistsAsync(role))
{
IR = await _roleManager.CreateAsync(new IdentityRole(role));
}
var user = await _userManager.FindByIdAsync(uid);
IR = await _userManager.AddToRoleAsync(user, role);
return IR;
}
catch (Exception exc)
{
throw;
}
}
}
public interface IInitialMigrationService
{
bool MigrateDataDump(DataDump dump);
}
}
Может кто-нибудь сказать мне, что я делаю не так?Я искал конкретный пример того, как такого рода вещи должны быть структурированы, но не смог найти ничего, кроме использования интерфейса (который, кажется, не помогает).
Спасибо.
--- РЕДАКТИРОВАТЬ --- Согласно предложению Camilos, я сделал следующие изменения:
MigrationController now starts like this:
public class MigrationController : Controller
{
private readonly ApplicationDbContext _context;
private UserManager<ApplicationUser> _userManager;
private RoleManager<IdentityRole> _roleManager;
private IConfiguration _configuration;
private IInitialMigrationService _initialMigrationService;
public MigrationController(ApplicationDbContext context, UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IConfiguration configuration, IInitialMigrationService initialMigrationService)
{
_context = context;
_userManager = userManager;
_roleManager = roleManager;
_configuration = configuration;
_initialMigrationService = initialMigrationService;
}
И сделал это дополнение к методу Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IInitialMigrationService, InitialMigrationService>();
Но япо-прежнему получаю ту же ошибку.
----- РЕДАКТИРОВАНИЕ 2 ---- Теперь я подозреваю, что проблема с использованием асинхронных методов.Кажется, что при запуске метода Asynch объект удаляется в следующий раз, когда я пытаюсь его использовать.
Например, в блоке ниже (для моего метода "MigrateUserSetup") я изменил "SaveChanges" на "SaveChangesAsync"", и в следующий раз при использовании dbcontext я получаю сообщение об ошибке:
private void MigrateUserSetup(DataDump dump)
{
dump.UserSetupList.ForEach(async delegate (DDUserSetup u)
{
if (string.IsNullOrEmpty(u.Email))
return;
var swUser = _context.SoftwareUser
.SingleOrDefault(du => du.OldID == u.ID);
if (swUser == null)
{
_context.SoftwareUser.Add(new Models.SoftwareUser
{
Name = u.Name,
// Migrate a bunch of fields
});
await _context.SaveChangesAsync(); // This was previously just "SaveChanges()", not Async
swUser = _context.SoftwareUser
.SingleOrDefault(du => du.OldID == u.ID); // Now I get the disposed object error here
string userID = await EnsureUserAsync(u.Password, u.Email, swUser.ID);
await EnsureRole(userID, ConstantData.ConstUserRole);
}
});
}
---- РЕДАКТИРОВАТЬ 3 ----
Я наконец заработал.Я закончил тем, что заменил цикл настройки пользователя из цикла «ForEach» на цикл «for», как показано ниже:
Оригинальный цикл «ForEach»:
dump.UserSetupList.ForEach(async delegate (DDUserSetup u)
{
Новый цикл «For»:
for (int i = 0; i < dump.UserSetupList.Count; i++)
{
var u = dump.UserSetupList[i];
Я не уверен, как это имеет такое большое значение, или действительно ли это желаемое решение, но может дать немного больше подсказки относительно основной проблемы.