entity framework将导航属性设置为null

我有一个entity framework数据库第一个项目。 这是一个模型的提取:

public partial class LedProject { public LedProject() { this.References = new HashSet(); this.Results = new HashSet(); this.History = new HashSet(); } public string Identifier { get; set; } public string Name { get; set; } public Nullable CompletionDate { get; set; } public System.DateTime CreationDate { get; set; } public System.Guid ProjectId { get; set; } public string Comment { get; set; } public virtual User ContactUser { get; set; } public virtual User CreationUser { get; set; } public virtual Customer Customer { get; set; } public virtual LedProjectAccounting Accounting { get; set; } public virtual LedProjectState State { get; set; } public virtual ICollection References { get; set; } public virtual ICollection Results { get; set; } public virtual User ResponsibleUser { get; set; } public virtual ICollection History { get; set; } } 
 public partial class User { public System.Guid UserId { get; set; } public string LoginName { get; set; } public System.DateTime CreationDate { get; set; } public string Firstname { get; set; } public string Lastname { get; set; } public string Email { get; set; } } 

我在设置LedProject类的导航项ResponsibleUserLedProject 。 当我将ResponsibleUser设置为另一个用户并随后保存DBContext的更改时,更改将存储在数据库中。

但是,当我想删除LedProject的当前ResponsibleUser时,通过将navigation属性设置为null。 更改不存储在数据库中。

 LedProject project = db.LedProject.Find(projectId); project.Name = string.IsNullOrEmpty(name) ? null : name; ... project.ResponsibleUser = responsibleUser == null ? null : db.User.Find(responsibleUser.UserId); ... db.SaveChanges(); 

有删除导航属性的技巧吗?

问题在于导航属性的延迟加载。 似乎该值首先设置为null,然后从数据库加载。 因此,期望的值(在我的情况下为null )被数据库中当前存储的值覆盖。

 LedProject project = db.LedProject .Include("ResponsibleUser") .Where(p => p.ProjectId == projectId) .FirstOrDefault(); 

这会在加载项目时加载ResponsibleUser 。 这终于解决了我的问题!

就像boindiil所说,问题在于延迟加载。 但是,您只需在要将其置零时加载该属性,以便Entity Framework机制知道它已更改。 代码可能如下所示:

 responsibleUser = responsibleUser == null ? null : db.User.Find(responsibleUser.UserId); if (responsibleUser == null) { // load the value to assure setting it to null will cause a change var dummy = project.ResponsibleUser; } project.ResponsibleUser = responsibleUser; ... db.SaveChanges(); 

我一直在想应该有一种方法可以使用db.ChangeTracker在没有负载的情况下强制进行保存但是我还没有找到它(而且我尝试过的一些东西看起来真的很hacky)。

找出最好的方法来做到这一点,而不必急于加载导航属性,这样你仍然可以使用EF的Find()而不必进行黑客攻击。

在导航属性旁边使用原始ID,其中类型是导航属性ID类型(通常是用户的字符串),例如:

 public partial class LedProject { public string ResponsibleUserId { get; set; } public virtual User ResponsibleUser { get; set; } } 

无论您在何处创建记录,都使用导航属性更新字符串,然后在要删除关系时,只需执行ledProject.ResponsibleUserId = null

如果你将id命名为导航属性name + id以外的其他内容,那么你需要使用注释或流畅的api进行映射。

更多信息: 在什么情况下我需要entity framework中的外键和导航属性

我遇到了这个问题,并提出了一个小的“黑客”,不会破坏延迟加载。

只需在此模型上定义属性,就像这样 –

  public int? AccountId { get; set; } //workaround for entity framework lazy loading problem Account account; public virtual Account Account { get { return account; } set { account = value; if (value == null) { AccountId = null; } } } 

现在您不必急于加载导航属性,将其设置为null将起作用。 更重要的是,通过将hack直接放在您的实体中,您不必记住在代码库中的任何其他位置进行显式检查。

从Entity Framework 5.0开始:

 db.Entry(project).Reference(p => p.ResponsibleUser).CurrentValue = null; 

https://msdn.microsoft.com/en-us/data/jj713564.aspx

您可以将所有导航属性设置为null,如下所示:(您需要拥有EF上下文),此处:import是IEnumerable

  foreach (var entity in imported) { foreach (var np in _YourEntityRepository.GetReferenceProperties()) entity.GetType().GetProperty(np.Name).SetValue(entity, null); } 

将GetReferenceProperties定义为:

 public IEnumerable GetReferenceProperties() { var oc = ((IObjectContextAdapter)Context).ObjectContext; var entityType = oc.MetadataWorkspace.GetItems(DataSpace.OSpace) .OfType() .FirstOrDefault(et => et.Name == typeof(TEntity).Name); if (entityType != null) { foreach (NavigationProperty np in entityType.NavigationProperties .Where(p => p.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One || p.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne)) { yield return np; } } } 

请参阅https://docs.microsoft.com/en-us/ef/ef6/fundamentals/relationships 。

创建和修改关系”部分说明了在分配和设置为null时,外键属性和导航属性的相关内容。

EF5和forward有一些变化,但它们的关键是定义外键属性,使关系不再是一个独立的关联 (缺少外键属性)。

作为另一种解决方法,我将两种方法编译成扩展方法:

 public static void SetToNull(this TEntity entity, Expression> navigationProperty, DbContext context = null) where TEntity : class where TProperty : class { var pi = GetPropertyInfo(entity, navigationProperty); if (context != null) { //If DB Context is supplied, use Entry/Reference method to null out current value context.Entry(entity).Reference(navigationProperty).CurrentValue = null; } else { //If no DB Context, then lazy load first var prevValue = (TProperty)pi.GetValue(entity); } pi.SetValue(entity, null); } static PropertyInfo GetPropertyInfo( TSource source, Expression> propertyLambda) { Type type = typeof(TSource); MemberExpression member = propertyLambda.Body as MemberExpression; if (member == null) throw new ArgumentException(string.Format( "Expression '{0}' refers to a method, not a property.", propertyLambda.ToString())); PropertyInfo propInfo = member.Member as PropertyInfo; if (propInfo == null) throw new ArgumentException(string.Format( "Expression '{0}' refers to a field, not a property.", propertyLambda.ToString())); if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType)) throw new ArgumentException(string.Format( "Expression '{0}' refers to a property that is not from type {1}.", propertyLambda.ToString(), type)); return propInfo; } 

这允许您提供DbContext(如果有),在这种情况下,它将使用最有效的方法并将Entry Reference的CurrentValue设置为null。

 entity.SetToNull(e => e.ReferenceProperty, dbContext); 

如果没有提供DBContext,它将首先延迟加载。

 entity.SetToNull(e => e.ReferenceProperty); 

注意,这个问题本质上是一个副本: 如果我第一次获取属性并且在首先使用实体​​框架代码时将外键设置为null,则 entity framework将仅将相关实体属性设置为“null”