简体   繁体   中英

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

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM