C# 如何在EntityFramework Core中使用关系数据更新对象

C# 如何在EntityFramework Core中使用关系数据更新对象,c#,entity-framework,entity-framework-core,C#,Entity Framework,Entity Framework Core,我使用了实体框架核心并更新了一些对象与另一个对象的关系 我拥有以下实体: 客户: public class Client { public int Id { get; set; } public string ClientId { get; set; } public string ClientName { get; set; } public List<ClientScope> Scopes { get; set; } } 关于模型创建: m

我使用了
实体框架核心
并更新了一些对象与另一个对象的关系

我拥有以下实体:

客户:

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

    public string ClientId { get; set; }

    public string ClientName { get; set; }

    public List<ClientScope> Scopes { get; set; }
}
关于模型创建:

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);
      }));
但是这种方法也可以尝试更新
客户端。我只想更新ClientScope并指定它存储在表单中的ClientId

如何更新上面的
ClientScope
最好的方法是什么

我已经尝试实现了
BaseRepository
,我想为每个实体实现如下内容:

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;
        }
}
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
}
}
公共类基存储库:IBaseRepository
地点:班级
其中TDbContext:DbContext
{
公共虚拟数据库集表=>\u dbContext.Set();
私有只读TDbContext_dbContext;
公共基础存储库(TDbContext dbContext)
{
_dbContext=dbContext;
}
公共虚拟异步任务UpdateAsync(TEntity实体)
{
表1.更新(实体);
wait_dbContext.saveChangesSync();
返回实体;
}
}
但是我不知道-如何正确地为这样的实体指定
update
方法


谢谢你的建议。

我不知道哪里有问题?通过DBContext对象,您可以访问除对象的属性之外的任何类对象,而不仅仅是使用简单的赋值运算符更改属性的值,并且只使用DBContext.SaveChanges()您的更改将在数据库列中进行更新,一点问题也没有。如果你想用单独的Update()方法进行更改,你可以用几行代码或者更复杂的方法来实现,但我无法想象你为什么需要这个?!如果我错过了什么,告诉我更多。祝你好运

我认为您的实体是错误的,因为您的ClientScope没有clientId(外键)


公共字符串ClientId{get;set;}

如注释中所述(谢谢,Ivan),EF需要“了解”要更新的对象

很抱歉,我手头没有任何东西可用于测试,但您的UpdateClientScope方法应该如下所示:

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

如果您从未计划通过存储库方法添加或修改相关实体,只需将所有其他实体的状态设置为
EntityState。未更改的
,例如:

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;
}
我更喜欢拥有可用的FK属性,我自己:

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);
公共类ClientScope
{
公共int Id{get;set;}
公共字符串作用域{get;set;}
公共客户端{get;set;}
//如果您愿意,请使用[ForeignKey(“客户端”)]
public int ClientId{get;set;}
}
//或者使用FluentAPI
modelBuilder.Entity()
.HasRequired(cs=>cs.Client)
.WithMany(c=>c.范围)
//指定外键
.HasForeignKey(cs=>cs.ClientId);
//现在,您可以通过设置ClientId属性来指定客户端
var scope=new ClientScope()
{ 
ClientId=1,
Id=1,
Scope=“测试范围”,
}
UpdateClientScope(范围);

您可以想象-我创建了一个具有一组属性的新客户端,但是如果我只想更新ClientScope,并且我只想指定Client.Id,我只想更新ClientScope,但是对象客户端也将被更新,并且客户端的所有属性都设置为默认值。如何指定只更新ClientScope而不更新客户端?:)嗯,我认为你们在客户端模型类中建立模型是不正确的,你们声明了List generic,ClientScope在generic中,然后在ClientScope模型类中,你们声明了客户端模型实例,说实话,我不明白为什么你们以两种模型和非常不同的方式链接这两个对象?为什么ClientScope是列表对象?为什么需要列出对象?目的是什么?在ClientScope模型客户端模型中,我用OnModelCreating方法中的当前设置更新了我的帖子。对于一个客户端,可能会有更多的ClientScope—为此,它可能会将List与ClientScope一起使用。ClientScope必须与一个客户端连接。请随意粘贴您对模型的想法。谢谢。只要做两个不同的模型就行了!您说过,当您想同时更新ClientScope时,您不想更新客户端模型,是吗?现在我们创建两个独立的类模型,一切都会好起来的。如果您希望客户端模型和ClientScope模型具有类似的属性,以便以后连接数据。您可以在SQL Server(或您正在使用的任何数据库)中执行此操作。无论哪种方式,我都看不到依赖于属性级别的目的。你的意思是-创建单独的模型和创建外键等。从模型中-例如,在sql management studio上手动创建?我已经在onmodel创建部分创建了外键-我不在这里粘贴-我的失败,对不起。ClientId不是主键,只是客户端的唯一标识符。这并不能回答这个问题。一旦你有足够的资源,你将能够胜任任何职位;相反,.用关系更新断开连接的实体不能用简单的通用方法来完成,这种方法只适用于没有导航属性的简单实体。请问,断开连接的实体是什么意思?上下文无法检索/跟踪的实体。像您的
var scope=new ClientScope(){Client=new Client{Id=1},Id=1,scope=“Test scope”}
scope
scope.Client
都已断开连接。与
var scope=\u dbContext.ClientScope.Include(x=>x.Client.FirstOrDefault(…)相反。在后一种情况下,EF知道数据库中存在
scope
scope.Client
,而在前一种情况下,EF完全不知道它们是什么,因此您必须手动正确地附加它们。这种技术称为存根实体,应该在上下文短暂且仅用于所讨论的操作时立即起作用。如果有骡子
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);