【问题标题】:Entity Framework code first item duplication issue实体框架代码第一项重复问题
【发布时间】:2015-12-04 07:34:17
【问题描述】:

我有一个用于将新用户写入数据库的 API 控制器,如下所示:

public abstract class ApiBaseController : ApiController
{        
    protected UserManager<User, Guid> UserManager { get; set; }

    protected dbcontext Repo { get; private set; }

    public ApiBaseController()
    {
        UserManager = HttpContext.Current.GetOwinContext().GetUserManager<XUserManager>();
        Repo = HttpContext.Current.GetOwinContext().Get<dbcontext>();
    }
}

public class UsersController : ApiBaseController
{
    [HttpPost]
    [FeatureAuthorization(FeatureName = "Users", Permission = FeaturePermissions.Write)]
    public IHttpActionResult Post([FromBody]CreateUserRequest req)
    {
        if (!ModelState.IsValid)
        {
            return ResponseMessage(Request.CreateResponse(HttpStatusCode.BadRequest, ModelState.ToApiResponse()));
        }

        var user = UserManager.FindByName(req.UserName);

        //some validation happens here

        user = new User()
        {
            AuthenticationMethod = req.AuthenticationMethod,
            DomainLDAP = req.DomainLDAP,
            EmailAddress = req.EmailAddress,
            FirstName = req.FirstName,
            SecondName = req.SecondName,
            MentionHandle = req.MentionHandle,
            MobileNumber = req.MobileNumber,
            UserName = req.UserName,
            IsDeleted = false
        };

        IdentityResult createResult = null;
        if (req.AuthenticationMethod == AuthenticationMethod.Internal)
        {
            createResult = UserManager.Create(user);
        }
        else
        {
            createResult = UserManager.Create(user, req.Password);
        }

        if (!createResult.Succeeded)
        {
            return ResponseMessage(Request.CreateResponse(HttpStatusCode.BadRequest, new ApiResponse(createResult.Errors.ToArray())));
        }

        //Map user to user type
        user.UserType = Repo.UserTypes.Find(req.UserTypeId);

        //Map user to area
        user.Area = Repo.Areas.Find(req.AreaId);

        //Map user to role
        var roles = new List<Role>();
        roles.Add(role);
        user.Roles = roles;

        //Map user to retailers
        user.Retailers = retailers;

        UserManager.Update(user);
        Repo.SaveChanges();

        var dto = Mapper.Map<UserDto>(user);

        return ResponseMessage(Request.CreateResponse(HttpStatusCode.Created, new ApiResponse<UserDto>(new[] { dto })));
    }
}

当我调试此方法并检查 user 对象时

UserManager.Update(user);

它的所有属性设置正确,包括用户类型、区域、角色和零售商的 GUID。但是,在执行该行并检查我的数据库后,已插入的用户对这些属性具有不同的 GUID,并且 EF 已将新行插入到与我的新用户相对应的用户类型和区域表中。

如果我删除执行更新的代码行并离开

Repo.SaveChanges();

行,user 正确插入了适当的用户类型和区域 ID,但是应该插入到我的 dbo.UserRoles 和 dbo.UserRetailers 桥接表中的行没有插入,这意味着用户和我分配给它的角色/零售商丢失了。

从我对 StackOverflow 的研究看来,我从数据库中获取的区域和用户类型对象似乎与上下文分离,因此更新创建了与我获取的对象具有相似属性的新对象,但是(显然) 具有不同的 ID。但是,我仍然不确定如何解决此问题。

【问题讨论】:

  • 我没有查看代码,但从您的问题描述看来,您似乎知道必须将对象附加到 DbContext。您可以这样做:stackoverflow.com/a/29721938/1864167 如果这解决了您的问题,请告诉我。

标签: c# sql entity-framework ef-code-first code-first


【解决方案1】:

在处理了与我们的实体类似的事情之后,我们更改了所有模型以始终在模型上为多对一关系提供可用的 ID,并且对于多对多关系始终使用托管表,而不是让实体框架处理它。

然后我们更新了代码以始终将 ID 应用于模型,而不是实际的其他对象。

例如:

型号:

    public class Organisation
    {
      public Guid Id { get; set; }
      public String Name { get; set; }
      public String Description { get; set; }
      //etc etc

      public Guid OrgTypeId { get; set; }

      #region Navigation properties

      public virtual OrgType OrgType { get; set; }

      #endregion

   }    

    public class OrgType 
    {
        public Guid Id { get; set; }
        public String Name { get; set; }
        public string Description { get; set; } 

        #region Navigation properties

        public virtual ICollection<Organisation> Organisations { get; set; }              

        #endregion
   }

映射:

    public class OrganisationMap : EntityTypeConfiguration<Organisation>
    {
        public OrganisationMap()
        {
            HasKey(t=>t.Id);
            Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            Property(t => t.Name).HasMaxLength(256);
            Property(t => t.Description).IsMaxLength().HasColumnType("ntext");                
            HasRequired(t => t.OrgType).WithMany(t => t.Organisations).HasForeignKey(t => t.OrgTypeId);
        }
    }

这可确保正确完成所有映射,向您公开外键,并允许您创建一个仅使用类型 ID 而非完整类型对象的新对象。

我相信即使在 SaveChanges() 之后,OrgType 对象可能仍然为 null,但是下次从上下文中获取它时会很好。

改变这一切需要一些工作,但从长远来看,访问主键/外键会使事情变得更容易。

希望有帮助!

【讨论】:

  • 感谢您的回复,我会相应地重构我的模型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-23
  • 2021-11-02
  • 1970-01-01
  • 1970-01-01
  • 2015-02-24
相关资源
最近更新 更多