使用Entity Framework插入主实体时插入相关实体

huangapple go评论68阅读模式
英文:

Inserting a dependent entity while inserting the principal entity with Entity Framework

问题

以下是你要翻译的代码部分:

Firstly, these are my entities.

public class Restaurant : Entity
{
    public string Name { get; set; }
    public int OwnerId { get; set; }
    public virtual Owner Owner { get; set; }
    public virtual Menu Menu { get; set; }

    public Restaurant()
    {
        Menu = new Menu();
    }
}
public class Menu : Entity
{
    public int RestaurantId { get; set; }
    public virtual ICollection<Campaign> Campaigns { get; set; }
    public virtual ICollection<Category> Categories { get; set; }
    public virtual Restaurant Restaurant { get; set; }

    public Menu()
    {
        Campaigns = new HashSet<Campaign>();
        Categories = a new HashSet<Category>();
    }
}

Also they have one to one relationship. Declared as:

modelBuilder.Entity<Restaurant>(r =>
{
    r.HasOne(r => r.Owner)
    .WithMany()
    .HasForeignKey(r => r.OwnerId);

    r.HasOne(r => r.Menu)
    .WithOne(m => m.Restaurant)
    .HasForeignKey<Menu>(m => m.RestaurantId);
});

modelBuilder.Entity<Menu>(m =>
{
    m.HasMany(m => m.Campaigns)
    .WithOne(c => c.Menu)
    .HasForeignKey(x => x.MenuId);

    m.HasMany(m => m.Categories)
    .WithOne(c => c.Menu)
    .HasForeignKey(c => c.MenuId);

    m.HasOne(m => m.Restaurant)
    .WithOne(r => r.Menu);
});

The problem is I am creating a Restaurant when I get the CreateRestaurantCommand request. Inside of it:

public class CreateRestaurantCommandHandler : IRequestHandler<CreateRestaurantCommand, CreatedRestaurantResponseDto>
{
    private readonly IRestaurantRepository _restaurantRepository;
    private readonly RestaurantBusinessRules _restaurantBusinessRules;

    public CreateRestaurantCommandHandler(IRestaurantRepository restaurantRepository, RestaurantBusinessRules restaurantBusinessRules)
    {
        _restaurantRepository = restaurantRepository;
        _restaurantBusinessRules = restaurantBusinessRules;
    }

    public async Task<CreatedRestaurantResponseDto> Handle(CreateRestaurantCommand request, CancellationToken cancellationToken)
    {
        await _restaurantBusinessRules.DoesOwnerExistAndHaveRestaurantWithSameName(request.OwnerId, request.RestaurantName);

        Restaurant restaurant = new()
        {
            Name = request.RestaurantName,
            OwnerId = request.OwnerId,
        };
        Restaurant createdRestaurant = await _restaurantRepository.CreateAsync(restaurant);

        CreatedRestaurantResponseDto response = new()
        {
            Id = createdRestaurant.Id,
            Name = createdRestaurant.Name,
            OwnerId = createdRestaurant.OwnerId
        };
        return response;
    }
}
Everythink works perfect. However the created Menu inside of the restaurant's constructor is not pushed to the db.

This my CreateAsync method.

public async Task<TEntity> CreateAsync(TEntity entity)
{
    Context.Entry(entity).State = EntityState.Added;
    await Context.SaveChangesAsync();
    return entity;
}

I have tried empty constructor creating the menu inside of curly brackets and changing the configuration made in dbcontext but these didn't solve the problem.

希望这能帮助你解决问题。

英文:

Firstly, these are my entities.

public class Restaurant : Entity
{
public string Name { get; set; }
public int OwnerId { get; set; }
public virtual Owner Owner { get; set; }
public virtual Menu Menu { get; set; }
public Restaurant()
{
Menu = new Menu();
}
}
public class Menu : Entity
{
public int RestaurantId { get; set; }
public virtual ICollection&lt;Campaign&gt; Campaigns { get; set; }
public virtual ICollection&lt;Category&gt; Categories { get; set; }
public virtual Restaurant Resturant { get; set; }
public Menu()
{
Campaigns = new HashSet&lt;Campaign&gt;();
Categories = new HashSet&lt;Category&gt;();
}
}

Also they have one to one relationship. Declared as:

modelBuilder.Entity&lt;Restaurant&gt;(r =&gt;
{
r.HasOne(r =&gt; r.Owner)
.WithMany()
.HasForeignKey(r =&gt;r.OwnerId);
r.HasOne(r =&gt; r.Menu)
.WithOne(m =&gt; m.Resturant)
.HasForeignKey&lt;Menu&gt;(m =&gt; m.RestaurantId);
});
modelBuilder.Entity&lt;Menu&gt;(m =&gt;
{
m.HasMany(m =&gt; m.Campaigns)
.WithOne(c =&gt; c.Menu)
.HasForeignKey(x =&gt; x.MenuId);
m.HasMany(m =&gt; m.Categories)
.WithOne(c =&gt; c.Menu)
.HasForeignKey(c =&gt; c.MenuId);
m.HasOne(m =&gt; m.Resturant)
.WithOne(r =&gt; r.Menu);
});

The problem is I am creating a Restaurant when I get the CreateRestourantCommand request. Inside of it:

public class CreateRestaurantCommandHandler : IRequestHandler&lt;CreateRestaurantCommand, CreatedRestaurantResponseDto&gt;
{
private readonly IRestaurantRepository _restaurantRepository;
private readonly RestaurantBusinessRules _restaurantBusinessRules;
public CreateRestaurantCommandHandler(IRestaurantRepository restaurantRepository, RestaurantBusinessRules restaurantBusinessRules)
{
_restaurantRepository = restaurantRepository;
_restaurantBusinessRules = restaurantBusinessRules;
}
public async Task&lt;CreatedRestaurantResponseDto&gt; Handle(CreateRestaurantCommand request, CancellationToken cancellationToken)
{
await _restaurantBusinessRules.DoesOwnerExistAndHaveRestaurantWithSameName(request.OwnerId, request.RestaurantName);
Restaurant restaurant = new()
{
Name = request.RestaurantName,
OwnerId = request.OwnerId,
};
Restaurant createdRestaurant = await _restaurantRepository.CreateAsync(restaurant);
CreatedRestaurantResponseDto response = new()
{
Id = createdRestaurant.Id,
Name = createdRestaurant.Name,
OwnerId = createdRestaurant.OwnerId
};
return response;
}
}

Everythink works perfect. However the created Menu inside of the restaurant's constructor is not pushed to the db.

This my CreateAsync method.

public async Task&lt;TEntity&gt; CreateAsync(TEntity entity)
{
Context.Entry(entity).State = EntityState.Added;
await Context.SaveChangesAsync();
return entity;
}

I have tried empty constructor creating the menu inside of curly brackets and changing the configuration made in dbcontext but these didn't solve the problem.

答案1

得分: 2

"EntityState = EntityState.Added"不会设置相关实体的状态,它只是将它们关联,但状态保持未修改。要确保实体及其相关实体被视为添加,请使用"Context.Set().Add(entity)"。这可能会在将来引起问题,因为在添加实体时,可能会有时希望添加新的相关实体,但有时希望关联相关实体。除非这些关联实体由DbContext跟踪,否则在尝试插入它们时可能会导致重复索引冲突,或者使用新的主键插入重复数据。

public async Task<TEntity> CreateAsync(TEntity entity)
{
    Context.Set<TEntity>.Add(entity);
    await Context.SaveChangesAsync();
    return entity;
}

我不建议使用通用存储库,而是建议使用强类型存储库,这样您可以根据需要处理特定的关系,并提供基本功能,如工厂方法,而不是由调用代码构造实体。

英文:

The short answer is that EntityState = EntityState.Added does not set the state of related entities, it associates them but leaves them Unmodified. Use Context.Set&lt;TEntity&gt;().Add(entity) to ensure the entity and related are treated as additions. This Will likely cause you issues down the road because in adding entities there will likely be times where you want to Add new related entities, but other times you want to Associate related entities. Unless those associated entities are tracked by the DbContext this can result in duplicate index violations as EF tries to insert them, or inserting duplicate data with new PKs.

public async Task&lt;TEntity&gt; CreateAsync(TEntity entity)
{
Context.Set&lt;TEntity&gt;.Add(entity);
await Context.SaveChangesAsync();
return entity;
}

I do not recommend using Generic Repositories, but instead strongly typed ones so you can deal with specific relationships as you need, and provide base functionality like Factory methods rather than having calling code constructing entities.

huangapple
  • 本文由 发表于 2023年3月1日 15:45:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/75600798.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定