使用实体框架使用外键将不同的记录插入表中

时间:2019-05-07 21:15:00

标签: c# asp.net entity-framework asp.net-web-api

这些是我的模型,我正在使用Entity Framework和代码优先方法。

public class Respondent
{
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int RespondentId { get; set; }
        public User Requester { get; set; }

        public User Provider { get; set; }
        public string Role { get; set; }
}

public class User: IUser
{
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int UserId { get; set; }
        public int UPI { get; set; }
        public string Name { get; set; }
}

这是两个生成的表的关系。

我想要的是如果用户表中存在用户,则不要创建该用户的另一个条目。

问题是我添加新的提供程序(用户类型)时,每次它在用户中创建新条目时,即使该用户存在,也是如此;我想要的是在受访者表中添加该用户的引用。

private Context db = new Context();

public async Task<IHttpActionResult> PostRespondent(Respondent respondent)
{
        var TempProvidersList = respondent.Providers.ToList();

        try
        {
            var requester = db.Users.Single(s => s.UserId == respondent.Requester.UserId);
            requester.IsPublic = false;
            var providerIds = respondent.Providers.Select(x => x.UPI).ToList();

            foreach (var providerId in providerIds)
            {
                if (!db.Respondents.Any(x => x.Requester.UserId == requester.UserId && x.Provider.UPI == providerId))
                {
                    var provider = respondent.Providers.Single(x => x.UPI == providerId);
                    provider.IsPublic = true;

                    db.Respondents.Add(new Respondent() { Requester = requester, Provider = provider, Role = provider.Role });
                }
            }

            db.SaveChanges();
        }
        catch (Exception ex)
        {
        }

        return CreatedAtRoute("DefaultApi", new { id = respondent.RespondentId }, respondent);
}

1 个答案:

答案 0 :(得分:2)

我认为问题是由于传递了由不同DbContext加载的实体,然后在新的DbContext中添加了对这些实体的引用。

这里的代码没有多大意义:

var res = db.Respondents
    .Include(user => user.Requester)
    .Include(user => user.Provider)
    .Where(o => o.Requester.UserId == requester.UserId ).ToList();

在代码中任何地方都不使用“ res”的地方。您是否确实使用了“ a”这个词? 首先,如果您只想检查是否存在一行,请使用Any()

if (!db.Respondents.Any(x => x.Requester.UserId == requester.UserId)) 

而不是加载所有可用实体,只是为了检查是否存在某些实体。

我看到的问题区域是这样:

tempProvider = TempProvidersList[i];
db.Respondents.Add(new Respondent() { Requester = requester, Provider = tempProvider, Role = TempProvidersList[i].Role });

这会将您的提供者引用设置为新的响应者中可能是从其他DbContext实例加载的用户(提供者)。

要解决此问题,您设置的所有引用都应从当前的DbContext中加载:

var providerIds = respondent.Providers.Select(x => x.UPI).ToList();
var providers = db.Users.Where(x => providerIds.Contains(x.UPI)).ToList();

var requester = db.Users.Single(s => s.UserId == respondent.Requester.UserId);
requester.IsPublic = false;
foreach(var providerId in providerIds)
{
   if (!db.Respondents.Any(x => x.Requester.UserId == requester.UserId)) 
   {
       var provider = providers.Single(x => x.UPI == providerId);
       provider.IsPublic = true;

       db.Respondents.Add(new Respondent() { Requester = requester, Provider = provider, Role = provider.Role });
   }
}
db.SaveChanges();

我选择根据ID在一次点击中加载所有适用的提供程序,而不是在循环内一次加载一次。在循环中,我只是尝试从已加载的集中检索与ID匹配的那个。

我会用try / catch块包装它并处理异常。可能想到的例外是找不到ProviderId的用户时。 (可以删除用户吗?)我对ID而不是已加载的引用进行了foreach操作,因为基于上述条件,该集合可能没有每个ID的实体。 (如果6个ID中只有5个加载了提供者用户,则ID集合将包含6个元素,而实体集合将只有5个元素)

这里的另一点是使用Single而不是FirstOrDefault来记录一条记录。这将强制执行该规则,并且如果存在多个或不匹配的记录,则将引发异常。仅当找不到行是预期结果时才应使用“ OrDefault”变体。如果数据库中有多个匹配项,FirstOrDefault将隐藏问题,并且如果没有“ OrderBy”子句,您将不能依赖将返回哪个引用。

相关问题