Tengo una aplicación básica de ASP.NET Core respaldada por un EF Core Code First DB. En pocas palabras, acabo de agregar un poco de funcionalidad nueva que manipula una colección de objetos de datos adjuntos a algún objeto principal, que se está cayendo por una razón aparentemente inexplicable.
Objeto padre:
public class Character
{
public Guid Id { get; set; }
// int/string properties omitted
public ICollection<CharacterMerit> CharacterMerits { get; set; }
}
Objeto en cuestión:
public class CharacterMerit
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public Guid CharacterId { get; set; }
public MeritKey MeritKey { get; set; } // enum
}
Esta lista se actualiza en dos lugares: creación de personajes y una acción separada dedicada a estos 'méritos'. La acción separada solo toma una lista de estos objetos CharacterMerit
como JSON, se asegura de que el objeto de carácter exista y los arroja a la capa del repositorio. No hay problemas aquí.
Puesta en marcha:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequireNonAlphanumeric = false;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
// Add application services.
services.AddScoped<ISheetsRepository, SheetsRepository>();
services.AddTransient<IViewModelFactory, ViewModelFactory>();
services.AddTransient<SeedAdminRole>();
}
Repositorio:
public bool UpdateMerits(Guid characterId, List<CharacterMerit> merits)
{
var dbCharacter = _dbcontext.Characters.Include(c => c.CharacterMerits).First(c => c.Id == characterId);
if (dbCharacter.CharacterMerits.Any())
{
_dbcontext.CharacterMerits.RemoveRange(dbCharacter.CharacterMerits);
dbCharacter.CharacterMerits = null;
_dbcontext.SaveChanges();
}
dbCharacter.CharacterMerits = merits;
_dbcontext.SaveChanges();
return true;
}
public bool AddOrUpdateCharacter(Character character, bool date = true)
{
if (date)
{
character.LastUpdated = DateTime.Now;
}
if (_dbcontext.Characters.FirstOrDefault(c => c.Id == character.Id) == null)
{
_dbcontext.Characters.Add(character);
}
else
{
_dbcontext.Characters.Update(character);
}
_dbcontext.SaveChanges();
return true;
}
El problema está en la creación del personaje. Crear acción:
[HttpPost]
public async Task<IActionResult> Create(BasicInfoViewModel viewModel)
{
var character = new Character
{
// property initialisation omitted for brevity
// Id property is not initialised here, EF does this
};
if(viewModel.Species != "Other")
{
character.CharacterMerits = new List<CharacterMerit>();
character.CharacterMerits.Add(new CharacterMerit
{
MeritKey = MeritKey.Default; // for sake of example
});
}
_repository.AddOrUpdateCharacter(character); // just adds the Character object to collection in dbcontext and calls SaveChanges()
// redirect to another action
}
Cuando intento ejecutar esta acción, aparece el siguiente error (que, curiosamente, no es capturado por el depurador y solo va directamente al navegador, DbUpdateException
en una DbUpdateException
). Parece estar cayendo en la llamada SaveChanges()
.
System.Data.SqlClient.SqlException: A transport-level error has occurred when receiving results from the server. (provider: Session Provider, error: 19 - Physical connection is not usable)
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
at System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()
at System.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()
at System.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()
at System.Data.SqlClient.TdsParserStateObject.TryReadByte(Byte& value)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior)
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, String executeMethod, IReadOnlyDictionary`2 parameterValues, Boolean closeConnection)
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteReader(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
ClientConnectionId:998c6223-efc9-4172-a344-4b2ba71cf4c9
Error Number:-1,State:0,Class:20
Si elimino el código que se agrega a la lista CharacterMerits
todo funcionará sin problemas. ¿Qué he intentado?
Character
recién creado de la base de datos. Character
en el controlador) y utilizando el método UpdateMerits () anterior Startup
He pensado en redireccionar esta acción a otra que lo realizaría, sin embargo, desde un punto de vista conceptual, este código pertenece a esta acción y no resolvería un problema fundamental, si es que existe. La aplicación se está ejecutando actualmente en un localdb, sin embargo, también hay una versión implementada que se ejecuta en una base de datos estándar de SQL Server. Esta no es la primera vez que trabajo con colecciones en EF, así que estoy completamente desconcertado en cuanto a lo que podría estar mal aquí.
Esta excepción parece indicar que la conexión se cayó. ¿Está utilizando Azure o algún otro proveedor de la nube? En cualquier caso, le sugiero que pruebe la función de resistencia de la conexión de EF Core.