當我嘗試添加具有新子實體的實體時,EntityFramework.dll中出現InvalidOperationException。EF 6.1.1和Web API添加實體時出現錯誤
我已經設置了一個小測試應用程序來試圖瞭解這個問題。
我有兩個模型:父母和孩子。
public class Parent
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ParentId { get; set; }
public String Name { get; set; }
public List<Child> Children { get; set; }
}
public class Child
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ChildId { get; set; }
public Guid ParentId { get; set; }
public string Name { get; set; }
// Navigation
[ForeignKey("ParentId")]
public Parent Parent { get; set; }
}
在的WebAPI側我ParentController
// PUT: api/Parents/5
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> PutParent(Guid id, Parent parent)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != parent.ParentId)
{
return BadRequest();
}
db.Entry(parent).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ParentExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
我已經一起引發一個WPF應用程序行使API的控制器。
點擊一個按鈕:
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
ParentApi parentApi = new ParentApi();
var response = await parentApi.GetParents();
if(response.ResponseCode.Equals(200))
{
var parent = ((List<Parent>)response.ResponseObject).Where(prnt => prnt.Name.Equals("Parent1", StringComparison.Ordinal)).Single();
if(parent != null)
{
// Put child entity/
if (parent.Children == null)
parent.Children = new List<Child>();
Child newChild = new Child();
newChild.Name = "Child One";
parent.Children.Add(newChild);
response = await parentApi.PutParent(parent.ParentId, parent);
if(response.ResponseCode.Equals(200))
{
// Success
Debug.WriteLine(response.ResponseObject.ToString());
}
else
{
// Other/
if (response.ResponseObject != null)
Debug.WriteLine(response.ResponseObject.ToString());
}
}
}
}
ParentAPi樣子:
public class ParentApi : ApiBase
{
public async Task<ApiConsumerResponse> GetParents()
{
return await GetAsync<Parent>("http://localhost:1380/api/Parents/");
}
public async Task<ApiConsumerResponse> PutParent(Guid parentId, Parent parent)
{
return await PutAsync<Parent>(parent, "http://localhost:1380/api/Parents/" + parentId);
}
}
ApiBase和ApiConsumerResponse樣子:
public class ApiBase
{
readonly RequestFactory _requester = new RequestFactory();
public async Task<ApiConsumerResponse> GetAsync<T>(string uri)
{
ApiConsumerResponse result = new ApiConsumerResponse();
try
{
var response = await _requester.Get(new Uri(uri));
result.ResponseCode = response.ResponseCode;
result.ReasonPhrase = response.ReasonPhrase;
if (result.ResponseCode == 200)
{
result.ResponseObject = await Task.Factory.StartNew(
() => JsonConvert.DeserializeObject<List<T>>(
response.BodyContentJsonString));
}
else
{
string msg = response.ReasonPhrase + " - " + response.BodyContentJsonString;
result.ErrorReceived = true;
}
}
catch (Newtonsoft.Json.JsonReaderException jsonE)
{
result.ErrorReceived = true;
}
catch (Exception e)
{
// Some other error occurred.
result.ErrorReceived = true;
}
return result;
}
public async Task<ApiConsumerResponse> PutAsync<T>(T apiModel, string uri)
{
ApiConsumerResponse result = new ApiConsumerResponse();
try
{
string json = await Task.Factory.StartNew(
() => JsonConvert.SerializeObject(
apiModel, Formatting.Indented));
var response = await _requester.Put(new Uri(uri), json);
result.ResponseCode = response.ResponseCode;
result.ReasonPhrase = response.ReasonPhrase;
// if 200: OK
if (response.ResponseCode.Equals(200))
{
result.ResponseObject = await Task.Factory.StartNew(
() => JsonConvert.DeserializeObject<T>(
response.BodyContentJsonString));
}
else
{
string msg = response.ReasonPhrase + " - " + response.BodyContentJsonString;
result.ErrorReceived = true;
}
}
catch (Newtonsoft.Json.JsonReaderException jsonE)
{
result.ErrorReceived = true;
}
catch (Exception e)
{
// Some other error occurred.
result.ErrorReceived = true;}
return result;
}
}
public class ApiConsumerResponse
{
public int ResponseCode { get; set; }
public string ReasonPhrase { get; set; }
public object ResponseObject { get; set; }
public bool ErrorReceived { get; set; }
}
RequestFactory(這是不是一個廠),它是響應類看起來像:
public class RequestFactory
{
public async Task<NetworkWebRequestMakerResponse> Get(Uri uri)
{
if (uri.UserEscaped)
{
uri = new Uri(Uri.EscapeUriString(uri.OriginalString));
}
using (var client = new HttpClient())
{
try
{
client.Timeout = TimeSpan.FromSeconds(60);
var response = await client.GetAsync(uri);
var stringResponse = await response.Content.ReadAsStringAsync();
return new NetworkWebRequestMakerResponse()
{
UnknownErrorReceived = false,
UnknonErrorExceptionObject = null,
ResponseCode = (int)response.StatusCode,
ReasonPhrase = response.ReasonPhrase,
BodyContentJsonString = stringResponse,
};
}
catch (Exception Ex)
{
return new NetworkWebRequestMakerResponse()
{
UnknownErrorReceived = true,
UnknonErrorExceptionObject = Ex,
ResponseCode = -1,
ReasonPhrase = "NONE",
BodyContentJsonString = "{NONE}",
};
}
}
}
public async Task<NetworkWebRequestMakerResponse> Post(Uri url, string json)
{
using (var client = new HttpClient())
{
HttpResponseMessage response;
try
{
Debug.WriteLine("POSTING JSON: " + json);
var content = new StringContent(json);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(@"application/json");
response = await client.PostAsync(url, content);
var stringResponse = await response.Content.ReadAsStringAsync();
/*
* For the reason given in the post below I will not call EnsureSuccessCode as it blows away the data
* http://stackoverflow.com/questions/14208188/how-to-get-the-json-error-message-from-httprequestexception
*
*/
// response.EnsureSuccessStatusCode();
return new NetworkWebRequestMakerResponse()
{
UnknownErrorReceived = false,
UnknonErrorExceptionObject = null,
ResponseCode = (int)response.StatusCode,
ReasonPhrase = response.ReasonPhrase,
BodyContentJsonString = stringResponse,
};
}
catch (Exception Ex)
{
return new NetworkWebRequestMakerResponse()
{
UnknownErrorReceived = true,
UnknonErrorExceptionObject = Ex,
ResponseCode = -1,
ReasonPhrase = "NONE",
BodyContentJsonString = "{NONE}",
};
}
}
}
public async Task<NetworkWebRequestMakerResponse> Put(Uri url, string json)
{
using (var client = new HttpClient())
{
HttpResponseMessage response;
try
{
Debug.WriteLine("PUTING JSON: " + json);
var content = new StringContent(json);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(@"application/json");
response = await client.PutAsync(url, content);
var stringResponse = await response.Content.ReadAsStringAsync();
/*
* For the reason given in the post below I will not call EnsureSuccessCode as it blows away the data
* http://stackoverflow.com/questions/14208188/how-to-get-the-json-error-message-from-httprequestexception
*
*/
// response.EnsureSuccessStatusCode();
return new NetworkWebRequestMakerResponse()
{
UnknownErrorReceived = false,
UnknonErrorExceptionObject = null,
ResponseCode = (int)response.StatusCode,
ReasonPhrase = response.ReasonPhrase,
BodyContentJsonString = stringResponse,
};
}
catch (Exception Ex)
{
return new NetworkWebRequestMakerResponse()
{
UnknownErrorReceived = true,
UnknonErrorExceptionObject = Ex,
ResponseCode = -1,
ReasonPhrase = "NONE",
BodyContentJsonString = "{NONE}",
};
}
}
}
public async Task<NetworkWebRequestMakerResponse> Delete(Uri url)
{
using (var client = new HttpClient())
{
HttpResponseMessage response;
try
{
response = await client.DeleteAsync(url);
var stringResponse = await response.Content.ReadAsStringAsync();
/*
* For the reason given in the post below I will not call EnsureSuccessCode as it blows away the data
* http://stackoverflow.com/questions/14208188/how-to-get-the-json-error-message-from-httprequestexception
*
*/
// response.EnsureSuccessStatusCode();
return new NetworkWebRequestMakerResponse()
{
UnknownErrorReceived = false,
UnknonErrorExceptionObject = null,
ResponseCode = (int)response.StatusCode,
ReasonPhrase = response.ReasonPhrase,
BodyContentJsonString = stringResponse,
};
}
catch (Exception Ex)
{
return new NetworkWebRequestMakerResponse()
{
UnknownErrorReceived = true,
UnknonErrorExceptionObject = Ex,
ResponseCode = -1,
ReasonPhrase = "NONE",
BodyContentJsonString = "{NONE}",
};
}
}
}
}
public class NetworkWebRequestMakerResponse
{
public bool UnknownErrorReceived { get; set; }
public Exception UnknonErrorExceptionObject { get; set; }
public int ResponseCode { get; set; }
public string ReasonPhrase { get; set; }
public string BodyContentJsonString { get; set; }
}
所以一切都很好。測試Get方法(未顯示)它返回父實體 - 好。
我遇到的問題是當我嘗試用一個新的子實體'放'一個父實體。如Button_Click方法中所示。
與新的孩子家長實體在parentController到達但是當我嘗試設置狀態修改:
db.Entry(parent).State = EntityState.Modified;
引發該錯誤:參照完整性約束衝突發生了:該屬性值(一個或多個)關係一端的'Parent.ParentId'與另一端的'Child.ParentId'的屬性值不匹配。
現在,作爲測試,我更改了控制器上的PUT方法以模擬來自客戶端的嘗試。
修改PUT方法:
public async Task<IHttpActionResult> PutParent(Guid id, Parent parent)
{
parent = db.Parents.Where(pe => pe.Name.Equals("Parent1", StringComparison.Ordinal)).Single();
var child = new Child();
child.Name = "Billy";
if (parent.Children == null)
parent.Children = new List<Child>();
parent.Children.Add(child);
db.Entry(parent).State = EntityState.Modified;
var result = await db.SaveChangesAsync();
Debug.Write(result.ToString());
}
這完美的作品。孩子被添加到數據庫它的ParentID被更新並且它自己的密鑰被生成。
那麼爲什麼碰到電線的物體會爆炸EF?
我試着先附加對象(db.Parents.Attach(parent);)但是會拋出相同的錯誤。
我很困惑。
謝謝你的指導。我現在發現我的自我閱讀由Julia Lerman和Rowan Millar編寫實體框架DbContext。在其他策略中,他們討論了與您在此處提供的方法非常類似的方法。目前我正在調查:可跟蹤實體:Tony Sneed通過codplex或visual studio gallery提供的對實體框架的N層支持。 – John 2014-10-29 14:59:06