I have a question about setting up Web Api 2 so that if there is a relationship between two entities, at least querying one of those includes the other entities in the result. It's hard to explain but very easy concept if you look at the example below:
So, Imagine here is what we have in the Entity Framework:
public class student {
public int Id {get; set;}
public string Name {get; set;}
public int School_Id {get;set;}
public virtual School School {get; set;}
}
And:
public class school {
public int Id {get; set;}
public string Name {get; set;}
public IEnumerable<Student> Students {get; set;}
}
So obviously this is a very simple example. It's clear that these two classes can cause a circular reference, is there an easy way for me to generate json output that from one side, includes the related property and from the other side it doesn't? (to prevent the circular reference), to make it clear, when I query schools, I want to get this:
[{Id:1, Name: "School A", Students: [{Id:1, Name:"Mike"}, {Id:2, Name: "Sheila"}]} ,
{Id:2, Name: "School B", Students: [{Id:3, Name:"Joe"}, {Id:4, Name: "Sarah"}]}]
And when I query students, I only get this (see there is no school):
[{Id:1, Name:"Mike", School_Id:1}, {Id:2, Name:"Sheila", School_Id:1}, {Id:3, Name:"Joe", School_Id:2}, {Id:4, Name:"Sarah", School_Id:2}]
There are a few ways to handle this situation, and the method you choose depends on a few factors. I'll list the common ways this is normally dealt with, and you can evaluate each to decide which makes sense for both your client and server configuration.
Use a View Model or anonymous projection. This method involves creating a unique class which has only the properties you wish to send to the client.
public class StudentVm {
public int Id {get; set;}
public string Name {get; set;}
public string SchoolName {get;set;}
}
Use the [JsonIgnore] attribute on properties you do not want to have JSON serialize. If you prefer the Opt-In approach, use [DataMember] on properties you wish to include in serialization.
public class student {
public int Id {get; set;}
public string Name {get; set;}
public int School_Id {get;set;}
[JsonIgnore]
public virtual School School {get; set;} //omitted
}
public class student {
[DataMember]
public int Id {get; set;}
[DataMember]
public string Name {get; set;}
[DataMember]
public int School_Id {get;set;}
public virtual School School {get; set;} //omitted by default
}
Use the initializer for the JSON Formatter to preserve references. A unique $id field will be added to each object, and any object which may cause a circular reference will be replaced with a $ref pointing to that object's $id value.
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.All;
[{"$id":"1", "Id":"1", "Name":"Mike", "School_Id":"1",
{"$id":"2", "Id":"1", "Name": "School A", "Students": [{"$ref":"1"}]
}]
Note that using a combination of these methods together is acceptable; You could, for example, use a View Model and use References Handling together.
References from http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization
Could you explicitly create objects in the form you want?
Schools.Select(s => new
{
s.Id,
s.Name,
Students = s.Students.Select(st => new {st.Id, st.Name})
})
Students.Select(st => new { st.Id, st.Name, st.School_Id })
Related
Trying to store a composite key table which is keyed for both fields to the table it defines dependencies for.
Example case
Import files: 1..10
Dependencies 1: 2,3; 2: 4,5; 4:10
Intent is to use this key-only table for code to do code first strongly typed definitions while also being light weight, and it seemed like the most straight forward way to do it before running into problems.
Current code:
public class ImportFileDependency
{
[Key]
[ForeignKey("ImportFile")]
public int ImportFileId { get; set; }
[ForeignKey("Id")]
public ImportFile ImportFile {get; set;}
[Key]
[ForeignKey("ImportFile")]
public int ImportFileDependencyId { get; set; }
[ForeignKey("Id")]
public ICollection<ImportFile> ImportFileDependencies { get; set; }
}
public class ImportFile
{
[Key]
public int Id {get; set;}
public string Name { get; set; }
public string WorkbookTab { get; set; }
public string File { get; set; }
public ICollection<ImportFileDependency> Dependencies { get; set; }
}
...
modelBuilder
.Entity<ImportFileDependency>(e =>{
e.HasKey(ifd => new { ifd.ImportFileId, ifd.ImportFileDependencyId });
e.HasOne(ifd => ifd.ImportFile)
.WithMany(i => i.Dependencies);
});
modelBuilder
.Entity<ImportFile>()
.HasMany(i => i.Dependencies)
.WithOne()
.HasForeignKey(z => z.ImportFileId);
...
After multiple revisions of following the responses of the add-migration exception response, currently on:
There are multiple properties pointing to navigation 'ImportFile' in entity type 'ImportFileDependency'. To define composite foreign key using data annotations, use ForeignKeyAttribute on navigation.
which did not update from the most recent iteration.
I seem to have recursed into a deadend so looking for guidance
Given the time you've asked it, you probably found the answer yourself or gave up on it, but if someone else struggles with this error, this solved my issue: Entity Framework Code First - two Foreign Keys from same table
You have to define the relationship using fluent API.
I'm trying to understand how to put this many to many annotation into fluent api. I just don't know the syntax to represent the column order.
public class UserNotification
{
[key]
[Column(Order = 1)]
public string UserId { get; set;}
[key]
[Column(Order = 2)]
public int NotificationId {get; set;}
public ApplicationUser User{get; set;}
public Notification Notification {get; set;}
}
I know fluent Api will look like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserNotification>()
.HasKey(n => new {n.UserId, n.NotificationId});
// What about the Column Order?
}
You can read the Key and Column data annotations as follows:
UserNotification has a key consisting of UserId and NotificationId columns, with UserId being first and NotificationId being second.
i.e. the column order attribute is used only to determine which column is first, second etc. in the context of the composite primary key.
Fluent API does not need that because you describe both the key columns and their order inside the HasKey expression:
modelBuilder.Entity<UserNotification>()
.HasKey(n => new { n.UserId, n.NotificationId });
// ^ ^
// first second
In other words, you did it correctly, no further action is needed.
I have a group model that has a many-to-many relationship with two other models. Here is a simplified code first to help explain:
public class Store
{
public int ID {get; set;}
public string Name {get; set;}
public virtual ICollection<StockGroup> StockGroups { get; set; }
}
public class StockItem
public int ID {get; set;}
public string Name {get; set;}
public virtual ICollection<StockGroup> StockGroups { get; set; }
}
public class StockGroup
public int ID {get; set;}
public string Name {get; set;}
public virtual ICollection<Store> Stores { get; set; }
public virtual ICollection<StockItem> StockItems { get; set; }
}
Using Fluent API I created a many-to-many relationship between Store and StockGroup as well as StockItem and StockGroup. This in turn creates two join tables.
The issue I am having is I cannot seem to create a query that spans from Store to StockItem or the reverse without using foreach loops.
For example: Given a single StockItem, I would like to know all the stores that are related to it through the StockGroup table. I tried something like this but what it is returning is a collection of a collection.
var stock = db.StockItems.Find(4);
var stores = stock.StockGroups.Select(g => g.Stores);
How can I rewrite this to combine the StockGroups to give me a collection of Stores?
Thank you!
You can use SelectMany() to flatten the hierarchy:
var stores = stock.SelectMany(s => s.StockGroups).SelectMany(g => g.Stores).ToList();
http://blogs.interknowlogy.com/2008/10/10/use-linqs-selectmany-method-to-flatten-collections/
I have a class Mailout with a Status that looks like this:
public class Mailout
{
public int Id {get; set; }
public string Name {get; set; }
public MailoutStatus Status { get; set; }
}
public class MailoutStatus
{
public int Id { get; set; }
public string Name { get; set;}
}
When I insert Mailouts and set the Status property, they are inserted correctly. When I fetch them, Status is always null. Since I don't have (and don't want) the status ID on my Mailout class, I have no way to retrieve it after-the-fact. How do I tell EF to populate this field eagerly, rather than lazily?
I'm hoping I can set something up in OnModelCreating() since I want this behavior all the time, not as an option that I can use sometimes by manipulating my LINQ-to-Entities queries.
You need to make your navigation properties virtual.
There is no such option in the ModelBuilder to configure an automatic eager loading of navigation properties in each query. You have to specify it query by query. As a workaround you could encapsulate eager loading in some method or property, for instance in the context:
public class MyContext : DbContext
{
public DbSet<Mailout> Mailouts { get; set; }
public IQueryable<Mailout> MailoutsWithStatus
{
get { return Mailouts.Include(m => m.Status); }
}
// ...
}
And then use in your queries:
context.MailoutsWithStatus.Where(...) ... etc.
Only an idea, it's untested.
Taking from Employee Info Starter Kit - upcoming MVC edition, here is a snippet, that works pretty well, to eager load objects when used:
public class Employee
{
...
public int? ReportsTo { get; set; }
[ForeignKey("ReportsTo")]
public virtual Employee Supervisor { get; set; }
/// <summary>
/// Children object collection of foreign key relation
/// </summary>
public virtual List<Employee> Subordinates { get; set; }
}
My question is similar to this one :
--> Many to one configuration using EF 4.1 code first
There are some fluent API solutions on google, with overriding "OnModelCreating" method and manually setting the foreign key options. But i would prefer a solution with data annotations if it is possible. Because I'd like to use inverse properties while coding. Such as TypeA object has got a TypeB object. So TypeB object should have a ParentTypeA property. Example :
public class User : IUser
{
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[RegularExpression(#"[A-Za-z0-9_\-\.]{2,32}"), MinLength(2), MaxLength(32)]
[Required(AllowEmptyStrings = false)]
public string UserName { get; set; }
// other props ....
// ....
public virtual UserGallery Gallery { get; set; }
}
public class UserGallery : IUserGallery
{
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserGalleryId { get; set; }
// other props ....
// ....
public virtual User ParentUser { get; set; }
}
A conventions way to do this in Code First is to use the UserID as the Primary Key of the UserGallery object. This is fine if its a true one to one relationship.
public class UserGallery : IUserGallery
{
[Key]
public int UserId {get;set;}
public User User {get;set;}
etc...
}
This has worked fine for me in the past.