PandaSharp
PandaSharp

Reputation: 642

EF Core Add a Related Entity (generic repository)

I have the an EF Core Db with the following models and one to many relationship

public class AttributeType
{
    public int AttributeTypeId { get; set; }
    public string Name { get; set; }

    public List<Attribute> AttributeList { get; set; }
}

public class Attribute
{
    public int AttributeId { get; set; }
    public string Name { get; set; }

    public int AttributeTypeId { get; set; }
    public AttributeType AttributeType { get; set; }
}

If I'll try to add an attribute in this way it works:

using (var db = new SampleDbContext())
            {
                var attributeType = db.AttributeTypes.FirstOrDefault();

                Attribute attribute = new Attribute
                {
                    Name = "bbb",
                    AttributeTypeId = attributeType.AttributeTypeId,
                    AttributeType = attributeType
                };
                var item = db.Attributes.Add(attribute);
                db.SaveChanges();
            }

(in the real the attributeType will come from a combobox and the name from a textbox, but now let's jus keep the code simple)

but when I try to use a generic method, like the following code, doesn't work

     AttributeType attributeType;

                using (var db = new SampleDbContext())
                {
                    attributeType = db.AttributeTypes.FirstOrDefault();
                }

                Attribute attribute = new Attribute
                {
                    Name = "eee",
                    AttributeType = attributeType,
                    AttributeTypeId = attributeType.AttributeTypeId
                };

                DbService.Insert(attribute);

 public static bool Insert<T>(T item) where T : class
        {
            try
            {
                using (var db = new SampleDbContext())
                {
                    db.Add(item);
                    db.SaveChanges();
                }
                return true;
            }
            catch (Exception exception)
            {
                Debug.WriteLine($"DbService Insert Exception: {exception}");
                return false;
            }

and I get the following exception

Exception thrown: 'Microsoft.EntityFrameworkCore.DbUpdateException' in Microsoft.EntityFrameworkCore.dll DbService Insert Exception: Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> Microsoft.Data.Sqlite.SqliteException: SQLite Error 19: 'UNIQUE constraint failed: AttributeTypes.AttributeTypeId'. at Microsoft.Data.Sqlite.Interop.MarshalEx.ThrowExceptionForRC(Int32 rc, Sqlite3Handle db) at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior) at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, String executeMethod, IReadOnlyDictionary2 parameterValues, Boolean closeConnection) at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteReader(IRelationalConnection connection, IReadOnlyDictionary2 parameterValues) at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection) --- End of inner exception stack trace --- at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(Tuple2 parameters) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IReadOnlyList1 entriesToSave) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess) at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess) at UwpSamples.Services.DbService.Insert[T](T item)

but if I don't pass the AttributeType or I set it to null, like the following code, it works:

AttributeType attributeType;

        using (var db = new SampleDbContext())
        {
            attributeType = db.AttributeTypes.FirstOrDefault();
        }

        Attribute attribute = new Attribute
        {
            Name = "ddd",
            AttributeType = null,
            AttributeTypeId = attributeType.AttributeTypeId
        };

        DbService.Insert(attribute);

-

AttributeType attributeType;

        using (var db = new SampleDbContext())
        {
            attributeType = db.AttributeTypes.FirstOrDefault();
        }

        Attribute attribute = new Attribute
        {
            Name = "ddd",
            AttributeTypeId = attributeType.AttributeTypeId
        };

        DbService.Insert(attribute);

Thanks

Upvotes: 2

Views: 1687

Answers (2)

AaronLS
AaronLS

Reputation: 38392

You are instantiating two separate contexts. One where you retrieve the AttributeType, and the second context inside the repository performing the insert will traverse the entity and believes the AttributeType is new and attempts to insert it. I recommend using a single Context per request(assuming this is behind a web request). There are workarounds that would involve flagging the entity as unchanged but you'd have to repeat the workaround each time and it'd be very specific to each model.

Usually you either use a Container or IoC framework to retrieve a Context per request, or you'd pass the context in the constructor of the repository. This way you can make multiple calls across different repositories and change tracking will work between the calls. It also simplifies scenarios where you have a transaction scope open across calls to different repositories.

Upvotes: 2

Ouarzy
Ouarzy

Reputation: 3043

According to the failure, the value of attributeType.AttributeTypeId already exists in the DB. Could you check what is this value and if it is indeed unique? I guess this value has to be unique because you use it as a primary key.

When you don't set the value or explicity set it to null, the ID is auto-generated by the DB, which explains why it works.

Hope it helps.

Upvotes: 1

Related Questions