Entity Framework Core: SqlException: A transport-level error has occurred when receiving results from the server

asp.net-core c# entity-framework entity-framework-core

Question

I have a basic ASP.NET Core application backed by an EF Core Code First DB. Simply put, I've just added a bit of new functionality that manipulates a collection of data objects attached to some parent object, which is falling over for seemingly no explicable reason.

Parent object:

public class Character
{
    public Guid Id { get; set; }
    // int/string properties omitted
    public ICollection<CharacterMerit> CharacterMerits { get; set; }
}

Object in question:

public class CharacterMerit
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public Guid CharacterId { get; set; }

    public MeritKey MeritKey { get; set; } // enum
}

This list is updated in two places - character creation, and a separate action dedicated to these 'merits'. The separate action just takes in a list of these CharacterMerit objects as JSON, makes sure that the character object exists and throws them into the repository layer. No problems here.

Startup:

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>();
}

Repository:

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;
}

The problem is on character creation. Create action:

[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
}

Whenever I try to execute this action I get the following error (which, interestingly, is not caught by the debugger and just goes straight to the browser, wrapped in a DbUpdateException). It appears to be falling over on the SaveChanges() call.

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

If I remove the code that adds to the CharacterMerits list everything will work without a fuss. What have I tried?

  • Moving list manipulation after the AddOrUpdateCharacter() call
  • The above, in addition to first retrieving the newly-created Character object from the DB.
  • Initialising a new list (instead of manipulating the Character object in the controller) and using the UpdateMerits() method above
  • Setting the repository to transient instead of scoped in Startup

I've thought about having this action redirect to another that would perform this, however from a conceptual point of view this code belongs in this action, and it would not solve a fundamental issue, if there is one. Application is currently running on a localdb however there is also a deployed version running on a standard SQL Server db. This is not the first time I have worked with collections in EF so I am completely baffled as to what could be wrong here.

1
0
8/30/2017 3:31:53 PM

Accepted Answer

This exception seems to indicate that the connection was dropped. Are you using Azure or any other cloud provider? In any case, I suggest you try the connection resiliency feature of EF Core.

2
8/30/2017 3:36:31 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow