我试图用实体框架保存状态消息,但遇到问题,每次我尝试保存来自已存在于数据库中的用户的推文时,出现异常!使用实体框架保存推文
{"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>
{
}
}
}
请问您可以分享您的状态和用户表的设置以及您正在使用的当前代码,更准确地说明您在做什么之前做的这个 db.status.Add(status); db.SaveChanges(); – 2014-11-09 01:33:40
我已经添加了您要求的代码片段! 我认为主要的问题是,我必须告诉EF用户需要更新,如果它已经存在。 但是,当我尝试使用“附加”它告诉我,“...同一类型的另一个实体已经具有相同的主键值。” 但据我所见,我完全按照这里描述的方案:http://msdn.microsoft.com/en-us/data/jj592676.aspx(将现有的但已修改的实体附加到上下文中) – 2014-11-09 11:47:46
Hi Miike,看起来棒极了,请问您只需添加TweetContext的设置,特别是用户和状态实体。我对他们的配置更感兴趣,所以我可以看到主键等等。 – 2014-11-09 12:37:09