Reputation: 3320
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;
}
}
How can I correctly specify the update
method for entities like this?
Upvotes: 4
Views: 8701
Reputation: 19
i think your entities false because your ClientScope has not clientId (Foreign Key).
public string ClientId { get; set; }
Upvotes: 0
Reputation: 16498
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);
Upvotes: 3
Reputation: 528
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
}
}
Upvotes: 2
Reputation: 226
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 !!!!
Upvotes: 1