为什么EF不能处理具有相同外键的两个属性,而是单独的引用/实例?

时间:2016-10-06 18:16:20

标签: entity-framework entity-framework-6

显然,EF6不喜欢具有多个使用相同键值的外键属性但不共享相同引用的对象。例如:

var user1 = new AppUser { Id = 1 };
var user2 = new AppUser { Id = 1 };

var address = new Address
{
    CreatedBy = user1, //different reference
    ModifiedBy = user2 //different reference
};

当我尝试插入此记录时,EF会抛出此异常:

Saving or accepting changes failed because more than one entity of type
'AppUser' have the same primary key value. [blah blah blah]

我发现这样做可以解决问题:

var user1 = new AppUser { Id = 1 };
var user2 = user1; //same reference

我可以编写一些帮助代码来规范化引用,但我更确切地说EF只是知道它们是基于ID的同一个对象。

至于为什么EF这样做,一种解释可能是它试图避免对同一对象进行多重CRUD操作,因为同一实体的单独实例可能包含不同的数据。我希望能够告诉EF不要担心这一点。

更新

所以我怀疑上面的上一段。由于没有办法告诉EF不要在任何一个实例上做CRUD,我现在就这样做:

if (address.ModifiedBy.Id == address.CreatedBy.Id)
{
    address.ModifiedBy = address.CreatedBy;
}

只要我不想在任何一个上做CRUD,就可以运行得很好。

UPDATE2

我以前曾这样做是为了防止EF在需要的时候验证其他所需的null属性是子实体的ID。但是,它不会使EF在具有相同ID的单独实例上进入tizzy。如果它不会对任何AppUser对象执行CRUD,为什么它关心实例是否不同?

foreach (var o in new object[] { address.ModifiedBy, address.CreatedBy })
{
    db.Entry(o).State = EntityState.Unchanged;
}

3 个答案:

答案 0 :(得分:0)

您可以添加两个额外属性以使主对象的Id为bash$> cat upper_lower.sh #!/bin/bash # Main loop to process the words passed to script in ARGV array for w in $@ do echo $w | while read word do # Matching lowercase only if [[ $word =~ ^[a-z] ]] then echo $word >> aa fi # Matching UPPERCASE only if [[ $word =~ ^[A-Z] ]] then echo $word >> bb fi # Matching digits only if [[ $word =~ ^[0-9] ]] then echo $word >> cc fi done done l=`cat aa|wc -l` # Taking lowercase count u=`cat bb|wc -l` # Taking uppercase count d=`cat cc|wc -l` # Taking digits count echo; echo "$l words are lowercase" # Added extra echo just to print new line. echo; echo "$u words are uppercase" echo; echo "$d words are digits" rm aa bb cc # removing temp files after processing bash$> ,然后您只能使用一个AppUser对象,并为已创建和已修改的属性引用它。

AppUser

否则,您的代码最终会通过使用相同的主键保存两个CreatedById = user1.Id, ModifiedById = user1.Id 实例。

另一种方法是将外键属性设置为仅一个AppUser对象

答案 1 :(得分:0)

如果从上下文中获得AppUser,那么您将不需要执行任何操作,因为实体框架将跟踪实体:

var user1 = context.AppUsers.Find(1);
var user2 = context.AppUsers.Find(1);

var address = new Address
{
    CreatedBy = user1, //different reference
    ModifiedBy = user2 //different reference
};

现在,他们都会指向相同的对象,不会导致冲突。

答案 2 :(得分:0)

解释是EF的更改跟踪器是identity map。即数据库中的记录映射到一个CLR对象,而且只有一个。

这可以通过尝试使用相同的键附加两个对象来轻松演示:

CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference

第二行会抛出异常:

  

附加' AppUser'失败,因为同一类型的另一个实体已具有相同的主键值。

如果您指定

,也会发生这种情况
user1

在此过程的某个地方,user2Id必须附加到上下文中,从而产生您获得的异常。

显然,您有一个函数可以接收两个可能不同或相同的AppUser值。不可否认,如果您只需从这些Id创建两个if (address.ModifiedBy.Id == address.CreatedBy.Id) 实例,就可以非常方便,而不必担心相同的密钥。不幸的是,你的解决方案......

 <section id="be" class="pad-xl">
      <div class="container"><!--tab contaire--> 
      
      
<ul id="tabs" class="nav nav-tabs nav-justified" role="tablist">
  <li  class="active"><a href="#plani" data-toggle="tab">La Planification</a></li>
  <li ><a href="#quali" data-toggle="tab">La qualité</a></li>
  <li ><a href="#prod" data-toggle="tab">La production</a></li>
  <li ><a href="#config" data-toggle="tab">Le configurateur</a></li>
</ul>
       
       <div class="tab-content"><!--tab content--> 
      
      
      
      
      
      
      
      
      
       <div role="tabpanel" class="tab-pane fade in active" id="plani"><!--tab panel--> 

      
        
        <div class="row"><!--2 Blocs--> 
        
 
	<div class="col-xs-4 feature wow fadeInLeft" data-wow-delay="0.3s"><!--FEATURES --> 
        
        <div class="list-group">
 
    <a href="#" data-seek="53.6" class="timecode list-group-item active">Introduction<span class="badge">14</span></a>
  
    <a href="#" data-seek="53.6" class="timecode list-group-item list-group-item-action">Blabla</a>
    <a href="#" data-seek="53.6" class="timecode list-group-item list-group-item-action">Blabla2</a>
    <a href="#" data-seek="53.6" class="timecode list-group-item list-group-item-action">Blabla3</a>  
    <a href="#" data-seek="53.6" class="timecode list-group-item list-group-item-action">Blabla4</a> 
    <a href="#" data-seek="53.6" class="timecode list-group-item list-group-item-action">5</a> 
		</div>
	</div>
		 
	<div class="col-xs-8 feature wow fadeInLeft" data-wow-delay="0.3s"><!--FEATURES --> 
        
	<div class="embed-responsive embed-responsive-16by9">
		 <iframe id="thevideo" class="embed-responsive-item" src="//player.vimeo.com/video/181964440?api=1" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
		
	</div
	</div></div>
        
 </div><!--2 Blocs--> 
        
        
        
        </div><!--tab panel--> 
        
         
         
         <div role="tabpanel" class="tab-pane fade" id="quali"><!--tab panel--> 

      
        
        <div class="row"><!--2 Blocs--> 
        
 
	<div class="col-xs-4 feature wow fadeInLeft" data-wow-delay="0.3s"><!--FEATURES --> 
        
        <div class="list-group">
 
    <a href="#" data-seek="53.6" class="timecode list-group-item active">Introduction<span class="badge">14</span></a>
  
    <a href="#" data-seek="53.6" class="timecode list-group-item list-group-item-action">PIC & Plan d’approvisionnement</a>
    <a href="#" data-seek="53.6" class="timecode list-group-item list-group-item-action">PDP</a>
    <a href="#" data-seek="53.6" class="timecode list-group-item list-group-item-action">Plan de charges</a>  
    <a href="#" data-seek="53.6" class="timecode list-group-item list-group-item-action">L’ordonnancement</a> 
    <a href="#" data-seek="53.6" class="timecode list-group-item list-group-item-action">Autres fonctionnalités Planification</a> 
		</div>
	</div>
		 
	<div class="col-xs-8 feature wow fadeInLeft" data-wow-delay="0.3s"><!--FEATURES --> 
        
	<div class="embed-responsive embed-responsive-16by9">
		 <iframe id="thevideo" class="embed-responsive-item" src="//player.vimeo.com/video/181964440?api=1" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
		
	</div>
	</div>
        
 </div><!--2 Blocs--> 
</div><!--tab panel--> 

   

</div>
<script>
jQuery(function ($) {
    var iframe = document.getElementById('thevideo');
    var player = $f(iframe);
    $('.timecode').on('click', function (e) {
        e.preventDefault();
        var seekVal = $(this).attr('data-seek');
        player.api('seekTo', seekVal);
    });
});
//@ sourceURL=pen.js
</script>
</section>

......是必要的。但是很坚固。