简体   繁体   中英

How update object with relation data in EntityFramework Core

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.

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 , eg:

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

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

I do not understand where is a problem ? through DBContext object you can reach any class object than object's property, than just change a value of property with simple assign operator and just DbContext.SaveChanges() your changes will be updated in Database column with no problem at all. If you want separate Update() method for changes you can do it with couple of code lines or more complex way with C# Reflection possibility but i can't imagine why would you need this ?! If i missed something tell me more. Good luck !!!!

i think your entities false because your ClientScope has not clientId (Foreign Key).

public string ClientId { get; set; }

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