I have the following scenario in my database. It is a record of Studies and those studies have other studies as prerequisites. In my DB design, it looks like this:
And my code looks something like this:
public class Study
{
public int ID { get; set; }
public string Topic { get; set; }
public byte TypeID { get; set; }
public virtual StudyType Type { get; set; }
public bool Deprecated { get; set; }
public virtual ICollection<Study> Prerequisites { get; set; }
}
public class StudyType
{
public byte ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Study> Studies { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Study> Studies { get; set; }
public DbSet<StudyType> StudyTypes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Study>()
.HasMany(p=>p.Prerequisites)
.WithMany().Map(ps =>
{
ps.ToTable("Prerequisites");
ps.MapLeftKey(x=>x.ID,"StudyID");
ps.MapRightKey(y=>y.ID,"PrerequisiteID");
});
}
I'm not super good at the EF syntax, but from what I've found Googling, that seems like it should work. Instead, I get Sequence contains more than one matching element.
I found this, but since the entity is referencing itself, I can't exactly rename the key field in only one of the tables: http://social.msdn.microsoft.com/Forums/eu/adonetefx/thread/745a2c4f-cb66-41ad-9524-15aa198c40c7
Anybody help me through this?
EDIT
Here is the full stack trace of the exception:
It executes on a line of LINQ: var x = from s in db.Studies select s;
Server stack trace:
at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
at System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.ManyToManyAssociationMappingConfiguration`2.Configure(DbAssociationSetMapping associationSetMapping)
at System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.NavigationPropertyConfiguration.Configure(DbDatabaseMapping databaseMapping)
at System.Data.Entity.ModelConfiguration.Utilities.IEnumerableExtensions.Each[T](IEnumerable`1 ts, Action`1 action)
at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigureAssociationMappings(DbDatabaseMapping databaseMapping)
at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.Configure(DbEntityTypeMapping entityTypeMapping, DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
at System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.Configure(DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
at System.Data.Entity.ModelConfiguration.ModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo, Boolean validateModel)
at System.Data.Entity.ModelConfiguration.ModelBuilder.Build(DbConnection providerConnection)
at System.Data.Entity.Internal.LazyInternalContext.CreateModel()
at System.Lazy`1.CreateValue()
Exception rethrown at [0]:
at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
at System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.ManyToManyAssociationMappingConfiguration`2.Configure(DbAssociationSetMapping associationSetMapping)
at System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.NavigationPropertyConfiguration.Configure(DbDatabaseMapping databaseMapping)
at System.Data.Entity.ModelConfiguration.Utilities.IEnumerableExtensions.Each[T](IEnumerable`1 ts, Action`1 action)
at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigureAssociationMappings(DbDatabaseMapping databaseMapping)
at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.Configure(DbEntityTypeMapping entityTypeMapping, DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
at System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.Configure(DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
at System.Data.Entity.ModelConfiguration.ModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo, Boolean validateModel)
at System.Data.Entity.ModelConfiguration.ModelBuilder.Build(DbConnection providerConnection)
at System.Data.Entity.Internal.LazyInternalContext.CreateModel()
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
at System.Data.Entity.Internal.Linq.InternalSet`1.get_Provider()
at System.Linq.Queryable.Select[TSource,TResult](IQueryable`1 source, Expression`1 selector)
at DataAccess.Sql.SqlStudyRepository.GetAll() in C:\Side Work\Rephidim Church\Tuchikos 2011\Program\DataAccess\Sql\SqlStudyRepository.cs:line 22
at API.Controllers.StudiesController.Index() in C:\Side Work\Rephidim Church\Tuchikos 2011\Program\API\Controllers\StudiesController.cs:line 24
at lambda_method(Closure , ControllerBase , Object[] )
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
This is what I have in an EntityTypeConfiguration<> implementation for a similar situation in CTP5.
HasMany(g => g.SubGroups)
.WithMany(g => g.ParentGroups)
.Map(m => m.ToTable("Groups_SubGroups"));
Not sure exactly how that translates to configuring the DbContext directly, but I imagine it should be close.
If memory serves, LeftKey() RightKey() syntax wasn't quite there in CTP5, so you just have to use the default column names that it creates or is expecting. In my case, it is GroupId and GroupId1. That follows the pattern <class>Id and <class>Id1, not <field> and <field>1 by the way.
The error that you're getting does seem familiar and I don't remember that the solution was obvious in any way. But, I did set this all up a while ago so the memories of how I arrived at something that works is a bit swiss cheesed. Hope it helps some.
Related
I'm trying to delete a record in the database by simply using db.'table'.remove('object') but when calling db.SaveChanges() exception is thrown.
'An exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll but was not handled in user code'
My deleting method in controller
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Customer customer = db.Customers.Find(id);
if (customer == null)
{
return HttpNotFound();
}
db.Customers.Remove(customer);
db.SaveChanges();
return RedirectToAction("Index");
}
Model:
public class Customer
{
[Key, ForeignKey("User")]
public int UserId { get; set; }
public string Email { get; set; }
public virtual CreditCard CreditCard { get; set; }
public virtual ShoppingCart ShoppingCart { get; set; }
public virtual List<Enquiry> Enquiries { get; set; }
public virtual List<Order> Orders { get; set; }
public virtual User User { get; set; }
}
public class User
{
[Key]
public int Id { set; get; }
public string Username { set; get; }
public string Password { set; get; }
public string FirstName { set; get; }
public string Surname { set; get; }
public virtual UserRole UserRole { get; set; }
}
public class ShoppingCart
{
[Key, ForeignKey("Customer")]
public int Id { get; set; }
public virtual List<CartItem> CartItems { get; set; }
public virtual Customer Customer { get; set; }
}
Part of the exception:
A DbUpdateException was caught while saving changes. Type: Customer_8D9A6B7F7D247C9CB9F95E830039791E299E94FC91841FD33610DD1804AEF739 was part of the problem.
Full exception:
System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.Entity.Core.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.ShoppingCarts_dbo.Customers_Id". The conflict occurred in database "MVC_COMP1562.Models.SchemaDBContext", table "dbo.ShoppingCarts", column 'Id'.
The statement has been terminated.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.<NonQuery>b__0(DbCommand t, DbCommandInterceptionContext`1 c)
at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func`3 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.NonQuery(DbCommand command, DbCommandInterceptionContext interceptionContext)
at System.Data.Entity.Internal.InterceptableDbCommand.ExecuteNonQuery()
at System.Data.Entity.Core.Mapping.Update.Internal.DynamicUpdateCommand.Execute(Dictionary`2 identifierValues, List`1 generatedValues)
at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
--- End of inner exception stack trace ---
at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.<Update>b__2(UpdateTranslator ut)
at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update[T](T noChangesResult, Func`2 updateFunction)
at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update()
at System.Data.Entity.Core.Objects.ObjectContext.<SaveChangesToStore>b__35()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(SaveOptions options, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction)
at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass2a.<SaveChangesInternal>b__27()
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction)
at System.Data.Entity.Core.Objects.ObjectContext.SaveChanges(SaveOptions options)
at System.Data.Entity.Internal.InternalContext.SaveChanges()
--- End of inner exception stack trace ---
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at MVC_COMP1562.Controllers.CustomersController.Delete(Nullable`1 id) in C:\Users\kacpe\Documents\Visual Studio 2015\Projects\MVC_COMP1562\MVC_COMP1562\Controllers\CustomersController.cs:line 165
It seems you have another Table called dbo.ShoppingCarts about a Shopping Cart, which have one or more rows related to the Customer you are trying to delete.
So first you must delete the rows related to that customer an then you can delete the customer. That happens because the Customer id is foreign key in that other table.
If that is no exactly the case, please add your ShoppingCarts class to the question.
I am trying to select a list of Invoices and their subclassed items.
Here are the models:
public class SalesContext : DbContext {
public DbSet<Product> Products { get; set; }
public DbSet<Invoice> Invoices { get; set; }
public DbSet<InvoiceItem> InvoiceItems { get; set; }
public SalesContext() : base ("DefaultConnection") {
}
}
public class Invoice {
public int Id { get; set; }
public List<InvoiceItem> Items { get; set; }
public Invoice() {
Items = new List<InvoiceItem>();
}
}
public abstract class InvoiceItem {
public int Id { get; set; }
public int Qty { get; set; }
}
public class ProductInvoiceItem : InvoiceItem {
public int ProductId { get; set; }
public Product Product { get; set; }
}
public class Product {
public int Id { get; set; }
public string Name { get; set; }
}
Here's the seed data:
internal sealed class Configuration : DbMigrationsConfiguration<SalesContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(SalesContext context) {
context.Products.AddOrUpdate(p => p.Name,
new Product {Name = "Bible - New Living Translation"},
new Product {Name = "Bible - King James Version"}
);
context.SaveChanges();
context.Invoices.AddOrUpdate(new Invoice {
Items =
new List<InvoiceItem>() {
new ProductInvoiceItem {ProductId = 1},
new ProductInvoiceItem {ProductId = 2}
}
}
, new Invoice {
Items =
new List<InvoiceItem>() {
new ProductInvoiceItem {ProductId = 1}
}
}
);
context.SaveChanges();
}
}
Here, I'm trying to select the data out with its associated subclass properties.
public class SalesController : Controller
{
private SalesContext db = new SalesContext();
// GET: Sales
public ActionResult Index() {
var query = db.Invoices.Include(i => i.Items);
var query2 = query.Include(i => i.Items.OfType<ProductInvoiceItem>().Select(pi => pi.Product));
var list = query2.ToList();
return View(list);
}
}
In the above code sample, query2 is broken. It throws a runtime exception.
System.ArgumentException was unhandled by user code
HResult=-2147024809 Message=The Include path expression must refer
to a navigation property defined on the type. Use dotted paths for
reference navigation properties and the Select operator for collection
navigation properties. Parameter name: path ParamName=path
Source=EntityFramework StackTrace:
at System.Data.Entity.QueryableExtensions.Include[T,TProperty](IQueryable1
source, Expression1 path)
at ParentChildEntityFrameworkStuff.Web.Controllers.SalesController.Index()
in c:\users\brandon.miller\documents\visual studio
2015\Projects\ParentChildEntityFrameworkStuff\ParentChildEntityFrameworkStuff.Web\Controllers\SalesController.cs:line
20
at lambda_method(Closure , ControllerBase , Object[] )
at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext
controllerContext, IDictionary2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext
controllerContext, ActionDescriptor actionDescriptor, IDictionary2
parameters)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.ActionInvocation.InvokeSynchronousActionMethod()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.b__39(IAsyncResult
asyncResult, ActionInvocation innerInvokeState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult2.CallEndDelegate(IAsyncResult
asyncResult)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase1.End()
at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult
asyncResult, Object tag)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult
asyncResult)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.b__3d()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.b__3f()
InnerException:
If I use query and get rid of the query2 stuff, it returns data, but of course leaves out the Product on the ProductInvoiceItems. The ProductId field is set, so I know it is getting at least the value types for the ProductInvoiceItems. I've tried and tried and searched and searched and have been unable to find a solution for this, arguably, common use-case.
What can I do in order to eagerly load the Product navigation property?
I had to change a one to many to a many to many relationship. With the former A user was assigned a companyId upon registering. The only documents they could return from the database was controlled with a where statement in my Web Api. Now that a User can be assigned many companies I need to change that where statement to do the same thing. So far I have created the junction table. I am having problems accessing it and returning it correctly.
Company Class
public class Company
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public int UserCompanyId { get; set; }
public virtual UserCompany UserCompany { get; set; }
}
UserClass
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity>
GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await manager
.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
public int UserCompanyId { get; set; }
public virtual UserCompany UserCompany { get; set; }
}
Junction Table
public class UserCompany
{
[Key]
public int UCompanyId { get; set; }
public string Id { get; set; }
public int CompanyId { get; set; }
}
ApiController
public IEnumerable<Document> GetDocuments()
{
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var user = manager.FindById(User.Identity.GetUserId());
using (var context = new ApplicationDbContext())
{
return context.UserCompanies
.Where(j => j.CompanyId == user.UserCompany.CompanyId)
.ToList();
}
}
The Error is coming at the .ToList()
Error 1 Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?)
Update
// GET api/<controller>
public List<Document> GetDocuments()
{
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var user = manager.FindById(User.Identity.GetUserId());
using (var context = new ApplicationDbContext())
{
return context.Documents
.Where(j => j.CompanyId == user.UserCompany.UCompanyId)
.ToList();
}
}
Document Class
public class Document
{
public int DocumentId { get; set; }
public DateTime DocumentDate { get; set; }
public string DocumentUrl { get; set; }
public DateTime DocumentUploadDate { get; set; }
public string DocumentUploadedBy { get; set; }
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
}
I changed IEnumberable to List. I am still not doing it right,I am getting a error in my ApiController
Non-static method requires a target.
I posted my Document Class as well. I am lost on how to make this work. First time with a many to many relationship
Here is the stacktrace
at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
at System.Data.Entity.Core.Objects.ELinq.QueryParameterExpression.TryGetFieldOrPropertyValue(MemberExpression me, Object instance, Object& memberValue)
at System.Data.Entity.Core.Objects.ELinq.QueryParameterExpression.TryEvaluatePath(Expression expression, ConstantExpression& constantExpression)
at System.Data.Entity.Core.Objects.ELinq.QueryParameterExpression.EvaluateParameter(Object[] arguments)
at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery1.<>c__DisplayClass7.b__6()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectQuery1.<>c__DisplayClass7.b__5()
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func1 operation)
at System.Data.Entity.Core.Objects.ObjectQuery1.GetResults(Nullable1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery1..GetEnumerator>b__0()
at System.Data.Entity.Internal.LazyEnumerator1.MoveNext()
at System.Collections.Generic.List1..ctor(IEnumerable1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)
at TransparentEnergy.ControllersAPI.apiDocumentUserController.GetDocuments() in c:\Development\TransparentEnergy\TransparentEnergy\ControllersAPI\apiDocumentUserController.cs:line 29
at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.b__9(Object instance, Object[] methodParameters)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)
I think it's because you're returning a List and the type your function return is IEnumerable, try:
public List<Document> GetDocuments()
{
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var user = manager.FindById(User.Identity.GetUserId());
using (var context = new ApplicationDbContext())
{
return context.UserCompanies
.Where(j => j.CompanyId == user.UserCompany.CompanyId)
.ToList();
}
}
Models:
public class Dog
{
[Key, DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
public DateTime? Birthdate { get; set; }
public string Color { get; set; }
public string Race { get; set; }
public string ChipNumber { get; set; }
public byte[] Photo { get; set; }
public virtual ICollection<Record> Records { get; set; }
public virtual User User { get; set; }
}
public class Record
{
public Record()
{
Quota = 1;
}
[Key, DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public DateTime? Time { get; set; }
[Required]
public string Name { get; set; }
public string Supplier { get; set; }
public double? Price { get; set; }
public int Quota { get; set; }
public string Note { get; set; }
public virtual Dog Dog { get; set; }
public virtual User User { get; set; }
}
WebapiConfig.cs:
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
in webapi calling to 'api/dogs' throws exception:
{"$id":"1","Message":"An error has occurred.","ExceptionMessage":"The
'ObjectContent1' type failed to serialize the response body for
content type 'application/json;
charset=utf-8'.","ExceptionType":"System.InvalidOperationException","StackTrace":null,"InnerException":{"$id":"2","Message":"An
error has occurred.","ExceptionMessage":"Error getting value from
'Records' on
'System.Data.Entity.DynamicProxies.Dog_2EE5CBDAE5320B6E973C971E716C24610E64BF4CECE05B6719C5876F271BA911'.","ExceptionType":"Newtonsoft.Json.JsonSerializationException","StackTrace":"
at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object
target)\r\n at
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter
writer, Object value, JsonContainerContract contract, JsonProperty
member, JsonProperty property, JsonContract& memberContract, Object&
memberValue)\r\n at
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter
writer, Object value, JsonObjectContract contract, JsonProperty
member, JsonContainerContract collectionContract, JsonProperty
containerProperty)\r\n at
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter
writer, Object value, JsonContract valueContract, JsonProperty member,
JsonContainerContract containerContract, JsonProperty
containerProperty)\r\n at
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter
writer, IEnumerable values, JsonArrayContract contract, JsonProperty
member, JsonContainerContract collectionContract, JsonProperty
containerProperty)\r\n at
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter
writer, Object value, JsonContract valueContract, JsonProperty member,
JsonContainerContract containerContract, JsonProperty
containerProperty)\r\n at
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter
jsonWriter, Object value, Type objectType)\r\n at
Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter
jsonWriter, Object value, Type objectType)\r\n at
Newtonsoft.Json.JsonSerializer.Serialize(JsonWriter jsonWriter, Object
value)\r\n at
System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type
type, Object value, Stream writeStream, Encoding
effectiveEncoding)\r\n at
System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type
type, Object value, Stream writeStream, Encoding
effectiveEncoding)\r\n at
System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type
type, Object value, Stream writeStream, HttpContent content)\r\n at
System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type
type, Object value, Stream writeStream, HttpContent content,
TransportContext transportContext, CancellationToken
cancellationToken)\r\n--- End of stack trace from previous location
where exception was thrown ---\r\n at
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.GetResult()\r\n at
System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()","InnerException":{"$id":"3","Message":"An
error has occurred.","ExceptionMessage":"An error occurred while
executing the command definition. See the inner exception for
details.","ExceptionType":"System.Data.Entity.Core.EntityCommandExecutionException","StackTrace":"
at
System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand
entityCommand, CommandBehavior behavior)\r\n at
System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlan.Execute[TResultType](ObjectContext
context, ObjectParameterCollection parameterValues)\r\n at
System.Data.Entity.Core.Objects.ObjectQuery1.<>c__DisplayClass7.b__6()\r\n
at
System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func1
func, IDbExecutionStrategy executionStrategy, Boolean
startLocalTransaction, Boolean releaseConnectionOnSuccess)\r\n at
System.Data.Entity.Core.Objects.ObjectQuery1.<>c__DisplayClass7.b__5()\r\n
at
System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func1
operation)\r\n at
System.Data.Entity.Core.Objects.ObjectQuery1.GetResults(Nullable1
forMergeOption)\r\n at
System.Data.Entity.Core.Objects.ObjectQuery1.Execute(MergeOption
mergeOption)\r\n at
System.Data.Entity.Core.Objects.DataClasses.EntityCollection1.Load(List1
collection, MergeOption mergeOption)\r\n at
System.Data.Entity.Core.Objects.DataClasses.EntityCollection1.Load(MergeOption
mergeOption)\r\n at
System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.Load()\r\n at
System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.DeferredLoad()\r\n
at
System.Data.Entity.Core.Objects.Internal.LazyLoadBehavior.LoadProperty[TItem](TItem
propertyValue, String relationshipName, String targetRoleName, Boolean
mustBeNull, Object wrapperObject)\r\n at
System.Data.Entity.Core.Objects.Internal.LazyLoadBehavior.<>c__DisplayClass72.b__1(TProxy
proxy, TItem item)\r\n at
System.Data.Entity.DynamicProxies.Dog_2EE5CBDAE5320B6E973C971E716C24610E64BF4CECE05B6719C5876F271BA911.get_Records()\r\n
at GetRecords(Object )\r\n at
Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object
target)","InnerException":{"$id":"4","Message":"An error has
occurred.","ExceptionMessage":"There is already an open DataReader
associated with this Command which must be closed
first.","ExceptionType":"System.InvalidOperationException","StackTrace":"
at
System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand
command)\r\n at
System.Data.SqlClient.SqlConnection.ValidateConnectionForExecute(String
method, SqlCommand command)\r\n at
System.Data.SqlClient.SqlCommand.ValidateCommand(String method,
Boolean async)\r\n at
System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior
cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String
method, TaskCompletionSource1 completion, Int32 timeout, Task& task,
Boolean asyncWrite)\r\n at
System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior
cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String
method)\r\n at
System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior
behavior, String method)\r\n at
System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior
behavior)\r\n at
System.Data.Common.DbCommand.ExecuteReader(CommandBehavior
behavior)\r\n at
System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.<Reader>b__c(DbCommand
t, DbCommandInterceptionContext1 c)\r\n at
System.Data.Entity.Infrastructure.Interception.InternalDispatcher1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget
target, Func3 operation, TInterceptionContext interceptionContext,
Action3 executing, Action3 executed)\r\n at
System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.Reader(DbCommand
command, DbCommandInterceptionContext interceptionContext)\r\n at
System.Data.Entity.Internal.InterceptableDbCommand.ExecuteDbDataReader(CommandBehavior
behavior)\r\n at
System.Data.Common.DbCommand.ExecuteReader(CommandBehavior
behavior)\r\n at
System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand
entityCommand, CommandBehavior behavior)"}}}}
solved.
created DTO of DOG and insted of returning a USER and RECORDS inside the DOGDTO it now return UserId and list of RecordIds
I am using code-first POCOs with EF4, CTP5. I've got a table with a lot of columns in it (over 100). I want to split the table into multiple types (aka "Table Splitting") so that I don't have to fetch all the data every time I want some basic information.
I can't seem to find any documentation on this using Google. I've found references to the concept, "Table Splitting", and I've also seen how to do it using an EDMX file, but no samples for code-first.
I was hoping it would be as simple as defining another entity type and relating them as you would any other navigation property like so...
public class User
{
public int UserID { get; set; }
public string UserName { get; set; }
[ForeignKey("UserID")]
public virtual UserDetails Details { get; set; }
}
public class UserDetails
{
public int UserID { get; set; }
public string MoreData { get; set; }
[ForeignKey("UserID")]
public virtual User User { get; set; } // nice to have, but not required
}
And I call it like this...
return (from u in Context.Users // <-- error occurs here
where u.UserID == userID
select u).FirstOrDefault();
Unfortunately this does not seem to be working. I am getting the following error when I do this...
[IndexOutOfRangeException: Index was outside the bounds of the array.]
System.Data.Entity.ModelConfiguration.Conventions.Edm.PropertyMaxLengthConvention.System.Data.Entity.ModelConfiguration.Conventions.Edm.IEdmConvention<System.Data.Edm.EdmAssociationType>.Apply(EdmAssociationType associationType, EdmModel model) +598
System.Data.Entity.ModelConfiguration.Configuration.EdmConventionDispatcher.Dispatch(TEdmDataModelItem item) +100
System.Data.Entity.ModelConfiguration.Configuration.EdmConventionDispatcher.VisitEdmAssociationType(EdmAssociationType item) +22
System.Data.Entity.ModelConfiguration.Edm.Services.DataModelItemVisitor.VisitCollection(IEnumerable`1 collection, Action`1 visitMethod) +267
System.Data.Entity.ModelConfiguration.Edm.Services.EdmModelVisitor.VisitAssociationTypes(EdmNamespace edmNamespace, IEnumerable`1 associationTypes) +75
System.Data.Entity.ModelConfiguration.Edm.Services.EdmModelVisitor.VisitEdmNamespace(EdmNamespace item) +91
System.Data.Entity.ModelConfiguration.Configuration.EdmConventionDispatcher.VisitEdmNamespace(EdmNamespace item) +32
System.Data.Entity.ModelConfiguration.Edm.Services.DataModelItemVisitor.VisitCollection(IEnumerable`1 collection, Action`1 visitMethod) +267
System.Data.Entity.ModelConfiguration.Edm.Services.EdmModelVisitor.VisitNamespaces(EdmModel model, IEnumerable`1 namespaces) +75
System.Data.Entity.ModelConfiguration.Edm.Services.EdmModelVisitor.VisitEdmModel(EdmModel item) +47
System.Data.Entity.ModelConfiguration.Configuration.EdmConventionDispatcher.VisitEdmModel(EdmModel item) +45
System.Data.Entity.ModelConfiguration.Configuration.ConventionsConfiguration.ApplyModel(EdmModel model) +254
System.Data.Entity.ModelConfiguration.ModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo, Boolean validateModel) +257
System.Data.Entity.ModelConfiguration.ModelBuilder.Build(DbConnection providerConnection) +172
System.Data.Entity.Internal.LazyInternalContext.CreateModel() +62
System.Lazy`1.CreateValue() +361
System.Lazy`1.LazyInitValue() +104
System.Lazy`1.get_Value() +89
System.Data.Entity.Internal.LazyInternalContext.InitializeContext() +358
System.Data.Entity.Internal.InternalContext.Initialize() +16
System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType) +16
System.Data.Entity.Internal.Linq.InternalSet`1.Initialize() +61
System.Data.Entity.Internal.Linq.InternalSet`1.get_Provider() +15
System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider() +13
System.Linq.Queryable.Where(IQueryable`1 source, Expression`1 predicate) +63
TourFactory.Web.AgentWebsite.Models.Websites.WebsiteRepository.GetUser(Int32 userID) in C:\Code\hdtf\TF4\Web\AgentWebsite\Models\Websites\WebsiteRepository.cs:28
TourFactory.Web.AgentWebsite.Controllers.WebsiteController.get_Agent() in C:\Code\hdtf\TF4\Web\AgentWebsite\Controllers\WebsiteController.cs:42
TourFactory.Web.AgentWebsite.Controllers.WebsiteController.OnActionExecuting(ActionExecutingContext filterContext) in C:\Code\hdtf\TF4\Web\AgentWebsite\Controllers\WebsiteController.cs:55
System.Web.Mvc.Controller.System.Web.Mvc.IActionFilter.OnActionExecuting(ActionExecutingContext filterContext) +39
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +81
System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +61
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +305
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +830
System.Web.Mvc.Controller.ExecuteCore() +136
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +232
System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +39
System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +68
System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +44
System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +42
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +141
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +54
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +61
System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +31
System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +56
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +110
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +38
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8841105
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184
Removing the User.Details property makes the error go away, but then I can't use the nifty navigation features of EF4.
Using Complex Types is the only way you can map a table to multiple types. However, Code First (and EF in general) does NOT support lazy/Deferred loading for complex types. In other words, EF will always populate the complex type each time you read the entity from the database:
public class User
{
public int UserID { get; set; }
public string UserName { get; set; }
public UserDetails Details { get; set; }
}
[ComplexType]
public class UserDetails
{
public string MoreData { get; set; }
}
The only way you can achieve lazy loading for your big table is by really splitting it into multiple tables and then you can use one-to-one associations to map it back to multiple entities.
For more info regarding Complex Types in EF Code First CTP, take a look at this article:
Associations in EF Code First CTP5: Part 1 – Complex Types
I know this question is more than a year but EF Code First supports table splitting and lazy loading at the same time.
Your model should be like this:
[Table("UserTable")]
public class User
{
[Key, ForeignKey("Details")]
public int UserID { get; set; }
public string UserName { get; set; }
public virtual UserDetails Details { get; set; }
}
[Table("UserTable")]
public class UserDetails
{
[Key, ForeignKey("User")]
public int UserID { get; set; }
public string MoreData { get; set; }
public virtual User User { get; set; }
}
Lazy loading (and eager loading) should work as expected.
Its important to note that table splitting works if
Entities have 1-to-1 relationship, and
Entities share the same Key
I guess the answer suggested by Rino is a better one. Though in the end you achieve same thing. But in the case of second one it's meant to do the exact same thing. First one can have problem when you cannot have apply [ComplexType] due to 3rd party code or you have a list of that type.
Use the second one or as in Fluent APIs as from Julia Lerman's book (splitting "People" table into "Person" and "PersonPhoto" entities.
modelBuilder.Entity<Person>().ToTable("People");
modelBuilder.Entity<PersonPhoto>().ToTable("People");