1

我試圖用實體框架保存狀態消息,但遇到問題,每次我嘗試保存來自已存在於數據庫中的用戶的推文時,出現異常!使用實體框架保存推文

{"A duplicate value cannot be inserted into a unique index. [ Table name = users,Constraint name = PK_dbo.users ]"} 

我完全理解爲什麼我得到這個錯誤,但不知道如何防止它!

要保存狀態消息我這樣做

版本1:

db.status.Add(status); 
db.SaveChanges(); 

每個狀態消息中包含完整的用戶信息作爲子實體。我用的模型(status.cs & user.cs)這段代碼:https://github.com/swhitley/TwitterStreamClient


從這個問題的信息:Save detached entity in Entity Framework 6 我想出了這個主意:

第2版:

user usr = db.user.Where<user>(u => u.id == status.user.id).SingleOrDefault(); 
if (usr == null) 
    db.user.Add(status.user); 
else 
{ 
    db.user.Attach(status.user); 
    db.Entry(status.user).State = EntityState.Modified; 
} 

db.status.Add(status); 
db.SaveChanges(); 

但這只是導致一個錯誤信息:

附加一個類型爲'TwitterStreamClient.user'的實體失敗,因爲 另一個相同類型的實體已經具有相同的主鍵 的值。如果 圖中的任何實體具有衝突的鍵值,則使用「附加」方法或將實體的狀態設置爲「未更改」或「已修改」時可能會發生這種情況。這可能是因爲某些實體 是新的並且尚未收到數據庫生成的密鑰值。在 這種情況下,使用'Add'方法或'Added'實體狀態來跟蹤圖表,然後根據情況將非新實體的狀態設置爲'Unchanged'或 'Modified'。


而這裏要求是我的狀態和用戶類別:

user.cs:

using System; 
using System.Globalization; 
using System.Runtime.Serialization; 
using System.ComponentModel.DataAnnotations; 

namespace TwitterStreamClient 
{ 
    [DataContract] 
    public class user 
    { 
     //<user> 
     //<id>1401881</id> 
     // <name>Doug Williams</name> 
     // <screen_name>dougw</screen_name> 
     // <location>San Francisco, CA</location> 
     // <description>Twitter API Support. Internet, greed, users, dougw and opportunities are my passions.</description> 
     // <profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/59648642/avatar_normal.png</profile_image_url> 
     // <url>http://www.igudo.com</url> 
     // <lang>en</lang> 
     // <protected>false</protected> 
     // <followers_count>1027</followers_count> 
     // <profile_background_color>9ae4e8</profile_background_color> 
     // <profile_text_color>000000</profile_text_color> 
     // <profile_link_color>0000ff</profile_link_color> 
     // <profile_sidebar_fill_color>e0ff92</profile_sidebar_fill_color> 
     // <profile_sidebar_border_color>87bc44</profile_sidebar_border_color> 
     // <friends_count>293</friends_count> 
     // <created_at>Sun Mar 18 06:42:26 +0000 2007</created_at> 
     // <favourites_count>0</favourites_count> 
     // <utc_offset>-18000</utc_offset> 
     // <time_zone>Eastern Time (US & Canada)</time_zone> 
     // <profile_background_image_url>http://s3.amazonaws.com/twitter_production/profile_background_images/2752608/twitter_bg_grass.jpg</profile_background_image_url> 
     // <profile_background_tile>false</profile_background_tile> 
     // <statuses_count>3390</statuses_count> 
     // <notifications>false</notifications> 
     // <following>false</following> 
     // <verified>true</verified> 
     // <contributors_enabled>false</verified> 
     //</user> 

     [DataMember] 
     [Key] 
     public string id { get; set; } 
     [DataMember] 
     public string name { get; set; } 
     [DataMember] 
     public string screen_name { get; set; } 
     [DataMember] 
     public string location { get; set; } 
     [DataMember] 
     public string description { get; set; } 
     [DataMember] 
     public string profile_image_url { get; set; } 
     [DataMember] 
     public string url { get; set; } 
     [DataMember] 
     public string lang { get; set; } 
     [DataMember] 
     public string @protected { get; set; } 
     [DataMember] 
     public string followers_count { get; set; } 
     [DataMember] 
     public string profile_background_color { get; set; } 
     [DataMember] 
     public string profile_text_color { get; set; } 
     [DataMember] 
     public string profile_link_color { get; set; } 
     [DataMember] 
     public string profile_sidebar_fill_color { get; set; } 
     [DataMember] 
     public string profile_sidebar_border_color { get; set; } 
     [DataMember] 
     public string friends_count { get; set; } 
     //save date only as string for now as DateTimeOffset is not supported 
     //public DateTimeOffset created_at_dt { get; set; } 
     [DataMember] 
     public string created_at 
     { 
      get ; //{ return created_at_dt.ToString("ddd MMM dd HH:mm:ss zzz yyyy"); } 
      set; //{ created_at_dt = DateTimeOffset.ParseExact(value, "ddd MMM dd HH:mm:ss zzz yyyy", CultureInfo.InvariantCulture);   } 
     } 
     [DataMember] 
     public string favourites_count { get; set; } 
     [DataMember] 
     public string utc_offset { get; set; } 
     [DataMember] 
     public string time_zone { get; set; } 
     [DataMember] 
     public string profile_background_image_url { get; set; } 
     [DataMember] 
     public string profile_background_tile { get; set; } 
     [DataMember] 
     public string statuses_count { get; set; } 
     [DataMember] 
     public string notifications { get; set; } 
     [DataMember] 
     public string following { get; set; } 
     [DataMember] 
     public string verified { get; set; } 
     [DataMember] 
     public string contributors_enabled { get; set; } 
    } 
} 

status.cs

using System; 
using System.Runtime.Serialization; 
using System.Globalization; 
using System.ComponentModel.DataAnnotations; 
using System.ComponentModel.DataAnnotations.Schema; 

namespace TwitterStreamClient 
{ 
    //<status> 
    //<created_at>Tue Apr 07 22:52:51 +0000 2009</created_at> 
    //<id>1472669360</id> 
    //<text>At least I can get your humor through tweets. RT @abdur: I don't mean this in a bad way, but genetically speaking your a cul-de-sac.</text> 
    //<source><a href="http://www.tweetdeck.com/">TweetDeck</a></source> 
    //<truncated>false</truncated> 
    //<in_reply_to_status_id></in_reply_to_status_id> 
    //<in_reply_to_user_id></in_reply_to_user_id> 
    //<favorited>false</favorited> 
    //<in_reply_to_screen_name></in_reply_to_screen_name> 
    //<geo/> 
    //<contributors/> 
    //</status> 

    [DataContract] 
    public class status 
    { 
     [DataMember] 
     [Key] 
     public string id { get; set; } 
     //save date only as string for now as DateTimeOffset is not supported 
     //public DateTimeOffset created_at_dt { get; set; } 
     [DataMember] 
     public string created_at 
     { 
      get; //{ return created_at_dt.ToString("ddd MMM dd HH:mm:ss zzz yyyy"); } 
      set; //{ created_at_dt = DateTimeOffset.ParseExact(value, "ddd MMM dd HH:mm:ss zzz yyyy", CultureInfo.InvariantCulture);   } 
     } 


     [DataMember] 
     public string text { get; set; } 
     [DataMember] 
     public string source { get; set; } 
     [DataMember] 
     public string truncated { get; set; } 
     [DataMember] 
     public string in_reply_to_status_id { get; set; } 
     [DataMember] 
     public string in_reply_to_user_id { get; set; } 
     [DataMember] 
     public string favorited { get; set; } 
     [DataMember] 
     public string in_reply_to_screen_name { get; set; } 
     [DataMember] 
     public user user { get; set; } 
     [DataMember] 
     public geo geo { get; set; } 
     [DataMember] 
     public string contributors { get; set; } 
    } 

    [DataContract] 
    public class geo 
    { 
     [Key] 
     public int geoId { get; set; } 
     [DataMember] 
     public string type { get; set; } 
     [DataMember] 
     public string[] coordinates { get; set; } 
    } 
} 

DataStore.cs

using System; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Linq; 
using System.Text; 

namespace TwitterStreamClient 
{ 
    class DataStore 
    { 
     static private TweetContext Db = null; 

     private static TweetContext getContext() { 
      if(Db == null) 
      { 
       Db = new TweetContext(); 
      } 
      return Db; 
     } 
     public static bool Add(status status) 
     { 
      TweetContext db = getContext(); 
      { 
       db.status.Add(status); 
       db.SaveChanges(); 
       return true; 
      } 
     } 
    } 
} 

TweetContext。CS

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Data.Entity; 

namespace TwitterStreamClient 
{ 
    class TweetContext : DbContext 
    { 
     public DbSet<status> status { get; set; } 
     public DbSet<user> user { get; set; } 

     static TweetContext() 
     { 
      // Database initialize 
      Database.SetInitializer<TweetContext>(new DbInitializer()); 
      using (TweetContext db = new TweetContext()) 
       db.Database.Initialize(false); 
     } 

     class DbInitializer : DropCreateDatabaseAlways<TweetContext> 
     { 
     } 
    } 
} 
+0

請問您可以分享您的狀態和用戶表的設置以及您正在使用的當前代碼,更準確地說明您在做什麼之前做的這個 db.status.Add(status); db.SaveChanges(); – 2014-11-09 01:33:40

+0

我已經添加了您要求的代碼片段! 我認爲主要的問題是,我必須告訴EF用戶需要更新,如果它已經存在。 但是,當我嘗試使用「附加」它告訴我,「...同一類型的另一個實體已經具有相同的主鍵值。」 但據我所見,我完全按照這裏描述的方案:http://msdn.microsoft.com/en-us/data/jj592676.aspx(將現有的但已修改的實體附加到上下文中) – 2014-11-09 11:47:46

+0

Hi Miike,看起來棒極了,請問您只需添加TweetContext的設置,特別是用戶和狀態實體。我對他們的配置更感興趣,所以我可以看到主鍵等等。 – 2014-11-09 12:37:09

回答

0

我發現做工精細一個辦法!

從上面的版本2中,我只需要將SingleOrDefault()更改爲Any<user>()

if(!db.user.Where<user>(u => u.id == status.user.id).Any<user>()) 
    db.user.Add(status.user); 
else 
{ 
    //db.user.Attach(status.user); 
    db.Entry(status.user).State = EntityState.Modified; 
} 
    db.status.Add(status); 
    db.SaveChanges(); 

我想與獲得用戶與SingleOrDefault()問題跟蹤與此ID的用戶因此Attach()拋出一個異常,因爲它已經跟蹤與此ID的用戶。但是當我使用Any<user>()它只是用這個鍵檢查用戶的存在,它不會跟蹤它!那麼可以附加用戶。

0

我有同樣的問題,做插入時,然後在那裏我得到的錯誤,我有重新初始化的DbContext 和我的問題解決了,我不理解,但對我來說

just reinitialize your dbcontext and check ....