How update object with relation data in EntityFramework Core

c# entity-framework entity-framework-core

Question

I have used the Entity Framework Core and update some object with relation to another object.

I have a following entities:

Client:

public class Client
{
    public int Id { get; set; }

    public string ClientId { get; set; }

    public string ClientName { get; set; }

    public List<ClientScope> Scopes { get; set; }
}

ClientScope:

public class ClientScope
  {
    public int Id { get; set; }

    public string Scope { get; set; }

    public Client Client { get; set; }
  }

OnModelCreating:

modelBuilder.Entity<Client>((Action<EntityTypeBuilder<Client>>) (client =>
      {
        client.ToTable<Client>(storeOptions.Client);
        client.HasKey((Expression<Func<Client, object>>) (x => (object) x.Id));
        client.Property<string>((Expression<Func<Client, string>>) (x => x.ClientId)).HasMaxLength(200).IsRequired(true);
        client.HasIndex((Expression<Func<Client, object>>) (x => x.ClientId)).IsUnique(true);
        client.HasMany<ClientScope>((Expression<Func<Client, IEnumerable<ClientScope>>>) (x => x.AllowedScopes)).WithOne((Expression<Func<ClientScope, Client>>) (x => x.Client)).IsRequired(true).OnDelete(DeleteBehavior.Cascade);
      }));

modelBuilder.Entity<ClientScope>((Action<EntityTypeBuilder<ClientScope>>) (scope =>
      {
        scope.ToTable<ClientScope>(storeOptions.ClientScopes);
        scope.Property<string>((Expression<Func<ClientScope, string>>) (x => x.Scope)).HasMaxLength(200).IsRequired(true);
      }));

I would like update the ClientScope for specific Client.Id for example Id = 1.

I have tried use this way:

public void UpdateClientScope(ClientScope scope){

_dbContext.ClientScope.Update(scope);
_dbContext.SaveChanges();
}

var scope = new ClientScope() { Client = new Client{Id = 1}, Id = 1, Scope = "Test Scope" }
UpdateClientScope(scope);

But this way try to update the Client as well. I want to update only ClientScope and specify ClientId which it is stored on my form.

What is the best way how update ClientScope above?

I have tried to implement the BaseRepository which I want to implement for every entity something like this:

public class BaseRepository<TDbContext, TEntity, TPrimaryKey> : IBaseRepository<TDbContext, TEntity, TPrimaryKey>
        where TEntity : class
        where TDbContext : DbContext
{
 public virtual DbSet<TEntity> Table => _dbContext.Set<TEntity>();

private readonly TDbContext _dbContext;

public BaseRepository(TDbContext dbContext)
        {
            _dbContext = dbContext;
        }

public virtual async Task<TEntity> UpdateAsync(TEntity entity)
        {
            Table.Update(entity);

            await _dbContext.SaveChangesAsync();

            return entity;
        }
}

But I don't know - How correctly specify the update method for entities like this?

Thank you for any suggestion.

1
3
9/21/2017 8:09:51 AM

Accepted Answer

If you never plan to add or modify a related entity via your repository methods you can simply set all other entities' state to EntityState.Unchanged, e.g.:

public virtual async Task<TEntity> UpdateAsync(TEntity entity)
{
    Table.Update(entity);

    foreach( var entry in _dbContext.ChangeTracker.Entries() )
    {
        if( entry.Entity != entity )
        {
            entry.State = EntityState.Unchanged;
        }
    }

    await _dbContext.SaveChangesAsync();

    return entity;
}

Alternatively, attach related entities as unchanged before calling the repo method(s). Perhaps create a repo method to return such an entity:

public TEntity GetEntityPlaceholder( int id )
{
    var entity = new TEntity() { Id = id };
    _dbContext.Attach( entity );
    return entity;
}

I prefer having the FK properties available, myself:

public class ClientScope
{
    public int Id { get; set; }

    public string Scope { get; set; }

    public Client Client { get; set; }
    // FK, use [ForeignKey( "Client" )] if you wish
    public int ClientId { get; set; }
}

// or use FluentAPI
modelBuilder.Entity<ClientScope>()
    .HasRequired( cs => cs.Client )
    .WithMany( c => c.Scopes )
    // specify foreign key
    .HasForeignKey( cs => cs.ClientId );

// now can specify a client by setting the ClientId property
var scope = new ClientScope() 
{ 
    ClientId = 1, 
    Id = 1, 
    Scope = "Test Scope",
}
UpdateClientScope(scope);
3
9/21/2017 11:46:28 AM

Popular Answer

As mentioned in the comments (thanks, Ivan), EF needs to 'know about' the object you want to update.

Sorry, I don't have anything to hand to test this with, but your UpdateClientScope method should look something like this:

public void UpdateClientScope(ClientScope scope){

// Get the existing object from the DB
ClientScope dbScope = _dbContext.ClientScope.FirstOrDefault(x => x.Id == scope.Id);

// Test it was in DB
if (dbScope != null)
{
  // Update the database object
  dbScope.Scope = scope.Scope;
  dbScope.Client = scope.Client;

  // SaveChanges works on dbScope
  _dbContext.SaveChanges();
}
else
{
  // Object not found, some error processing
}
}


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