如何在定义实体关系时为依赖操作指定有效排序?

Dav*_*Dev 6 c# entity-framework

我正在尝试对Advertisement包含一个对象的对象进行Upsert List<AdImage>.该Advertisement包含对应于一个外键User.A user可以具有零或更多Advertisements,并且Advertisement具有一个或多个AdImages.

upsert失败,并带有以下内容:

保存不公开其关系的外键属性的实体时发生错误.EntityEntries属性将返回null,因为无法将单个实体标识为异常的来源.通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常.有关详细信息,请参阅InnerException.

内在的例外是:

无法确定相关操作的有效排序.由于外键约束,模型要求或存储生成的值,可能存在依赖关系.

advertisement实例化非常简单的:

var ad = new Advertisement
{
    AdImages = new List<AdImage>
    {
        new AdImage {Image = model.Image}
    },

    Message = model.Message,
    Title = model.Title,
    User = user,
};  

_aAdAppService.UpsertAdvertisement(ad);
Run Code Online (Sandbox Code Playgroud)

有问题的实体定义为:

public class User : AbpUser<Tenant, User> 
{ // AbpUser is a 3rd party class which defines Id as a primary key

    public string AccessToken { get; set; }
    public long UserId { get; set; }

    public virtual List<Advertisement> Advertisements { get; set; }
}

public class Advertisement : Entity
{
    [Key]
    public long Id { get; set; }

    public string Title { get; set; }
    public string Message { get; set; }
    public List<AdImage> AdImages { get; set; }

    public virtual User User { get; set; }
}

public class AdImage : Entity
{
    [Key]
    public int Id { get; set; }
    public string Image { get; set; }

    public virtual Advertisement Advertisement { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这就是定义关系的方式:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
        .HasMany(u => u.Advertisements)
        .WithRequired(x => x.User);

    modelBuilder.Entity<Advertisement>()
        .HasMany(a => a.AdImages)
        .WithRequired(x => x.Advertisement);

    modelBuilder.Entity<AdImage>()
        .HasRequired(x => x.Advertisement);

    base.OnModelCreating(modelBuilder);

}
Run Code Online (Sandbox Code Playgroud)

错误消息是什么意思?我看不出我的关系是如何被错误定义的.我该如何解决这个问题?

Dav*_*Dev 1

解决方案是将我的端点包装在UnitOfWork. 我不太熟悉实体框架的经文,无法准确描述问题或为什么它有效,但它确实有效。

这是一个有效的示例(尽管与上面的示例代码略有不同):

[UnitOfWork]
public async void Post(AdvertisementVM model)
{
    CheckModelState();

    try
    {
        if (_unitOfWorkManager.Current == null)
        {
            using (var mgr = _unitOfWorkManager.Begin())
            {
                await ExecuteMultipleDatabaseCalls(model);

                await mgr.CompleteAsync();
            }
        }
        else
        {
            await ExecuteMultipleDatabaseCalls(model);
        }
    }
    catch (Exception ex)
    {
        throw new HttpException((int)HttpStatusCode.InternalServerError, ex.Message);
    }
}

private async Task ExecuteMultipleDatabaseCalls(AdvertisementVM model)
{
    var retailer = _retailerAppService.GetForUser(model.UserId);

    var ad = new Advertisement
    {
        Message = model.Message,
        Title = model.Title,

        Retailer = retailer
    };

    await _adAppService.InsertOrUpdate(ad);

    await _unitOfWorkManager.Current.SaveChangesAsync();
}
Run Code Online (Sandbox Code Playgroud)

UnitOfWork属性是 ASP.NET Boilerplate 项目的成员,其定义如下:

摘要: 该属性用于指示声明方法是原子的并且应被视为一个工作单元。具有此属性的方法将被拦截,在调用该方法之前打开数据库连接并启动事务。在方法调用结束时,如果没有异常,则提交事务并将所有更改应用于数据库,否则将回滚。

备注: 如果在调用该方法之前已经存在工作单元,则该属性无效,如果存在,则使用相同的事务。