I have DB that contains 3 tables - Actors, Films, Actors_Films. 2 of the tables have a many-to-many relationship (Actors and Films), which is modelled using a junction table (Actors_Films).
I'm using EF4 in a Silverlight app. I've created a EF model, and the edmx designer shows just my Ac tors and Films entities, but they each have a navigation property to the other entity (Actors has a navigation property of Films, and Films has a navigation property of Actors).
I've added a domain service, and built the project. Using Actors as an example I now want to add a view that contains a dataform that will let me cycle through Actors, and a datagrid that will show any films the currently selected actor has appeared in. However, in the Data source tab, I have a domain context containing 2 entities - Actors and Films. These 2 entities are only showing their actual columns, the navigation properties aren't appearing:
Actors
---ActorID
---ActorName
Films
---FilmID
---FilmTitle
Is this correct? I thought the navigation properties should show up.
My actual application is more complicated than this, but this is a simplified example just to focus on the actual issue.
Thanks
Mick
WCF Ria Services don't support Many To Many Relation. You must have association table on edmx. In order that Navigate properties appear on client you must add [Include] attribute to navigate property in apropriate metadata of Entity. The metadata usually generated when you create any DomainService. For example, we have relation many to many ContractPosition and OrderPosition:
//ContractPositionsService.metadata.cs
[MetadataTypeAttribute(typeof(ContractPosition.ContractPositionMetadata))]
public partial class ContractPosition
{
internal sealed class ContractPositionMetadata
{
public int ContractPositionId { get; set; }
[Include]
public EntityCollection<ContractToOrderLink> ContractToOrderLinks { get; set; }
...
}
//ContractToOrdersLinksService.metadata.cs
[MetadataTypeAttribute(typeof(ContractToOrderLink.ContractToOrderLinkMetadata))]
public partial class ContractToOrderLink
{
internal sealed class ContractToOrderLinkMetadata
{
[Include]
public ContractPosition ContractPosition { get; set; }
public int ContractPositionId { get; set; }
[Include]
public OrderPosition OrderPosition { get; set; }
public int OrderPositionId { get; set; }
}
}
//OrderPositionsService.metadata.cs
[MetadataTypeAttribute(typeof(OrderPosition.OrderPositionMetadata))]
public partial class OrderPosition
{
internal sealed class OrderPositionMetadata
{
public int OrderPositionId { get; set; }
[Include]
public EntityCollection<ContractToOrderLink> ContractToOrderLinks { get; set; }
...
}
}
Related
I want to store multiple values from a dropdown using .NET Core MVC and Entity Framework. I have no idea how to do that. This is my model code.
public class Project
{
[Key]
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string? Description { get; set; }
public List<int> SkillsID { get; set; }
[ValidateNever]
public List<Skill> skills { get; set; }
}
Start here (https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key ) to understand relationships in EF. In your case you will likely want to set up a Many-to-Many relationship between Projects and Skills. This assumes you will have a list of Skills where by each Project associates itself with 0, 1, or many of those skills. In this case you would have classes like:
public class Project
{
// ... project properties.
public virtual ICollection<Skill> Skills { get; protected set; } = new List<Skill>();
}
public class Skill
{
// ... skill properties.
}
When mapped, you tell EF that Project .HasMany(x => x.Skills).WithMany() as we likely don't need a Projects collection on each individual Skill entity. The last step is telling EF how to associate these two entities. In Many-to-Many relationships this involves a joining table such as a ProjectSkills table:
[tbl:ProjectSkills]
- PK,FK - ProjectId
- PK,FK - SkillId
You don't track lists of FKs in the entity. Think of it from a database perspective. When relating tables together with FKs, you don't have an array or such of FKs within a single row. You use a joining table.
The PK for the table is a composite between the Project ID and Skill ID, where each of those is a FK back to the corresponding table. EF can create this table by convention or you can configure it manually if you want to fine-tune the naming.
If you want to track more detail about the relationship such as tracking a CreatedDate etc. then you will need to map the relationship as an entity which would look like:
public class Project
{
// ... project properties.
public virtual ICollection<ProjectSkill> ProjectSkills { get; protected set; } = new List<ProjectSkill>();
}
public class Skill
{
// ... skill properties.
}
public class ProjectSkill
{
[Key, Column(Order = 0)]
public int ProjectId { get; set; }
[Key, Column(Order = 1)]
public int SkillId { get; set; }
public DateTime CreatedDateTime { get; set; }
// .. Other details about the relationship.
[ForeignKey("ProjectId")]
public virtual Project Project { get; set; }
[ForeignKey("SkillId")]
public virtual Skill Skill { get; set; }
}
You may come across examples for EF core using these joining entities as this was required in earlier versions of EF Core (2, 3.1) for all Many-to-Many relationships.
For a One-to-Many where skills specifically belong to a Project, then a ProjectID would be put into the Skill table. The Project entity would have the same collection of Skills, but the mapping would be a: .HasMany(x => x.Skills).WithOne() Where the Skill table would contain a ProjectID to associate itself to a given project. EF can represent this relationship as a one-way where Project has the collection and Skill doesn't expose a reference to Project, or bi-directional where you can add a Project reference into Skill. (.HasMany(x => x.Skills).WithOne(x => x.Project))
I have this class
public class Teacher
{
public int Id{ get; set; }
public ICollection<Product> Products{ get; set; }
}
Below code is from the designer class generated by the EF migrations
modelBuilder.Entity("XYZ.Models.Entities.Product", b =>
{
b.HasOne("XYZ.Models.Entities.Teacher")
.WithMany("Courses")
.HasForeignKey("TeacherId");
});
I've checked my database, there is no such thing that shows where the Products IDs stored or anything, yet it works!
Where does that info get stored?
I have a detail view corresponding to a user entity. Each user entity has one or more comment entities, which is represented on the detail view as a grid.
So following EF convention, the user model has a UserComments member to represent the relation:
public partial class User
{
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<UserComments> UserComments { get; set; }
//....
}
When it came time to create the user comments grid in the user detail view, I realized that the grid does not properly bind to an ICollection (couldn't add new rows to the grid). After some digging, I found that I needed to use an ObservervableColletion. Ok, so I converted my ICollection into an ObserverableCollection....
public class UserDetailViewModel
{
public virtual User UserData { get; set; }
private ObservableCollection<UserComments> _UserComments;
public ObservableCollection<UserComment> UserComments {
get { return _UserComments; }
}
public void Load(int UserID)
{
this.UserData = UserRepo.Find(UserID);
this._UserComments = new ObservableCollection<UserComment>(UserData.UserComments);
}
}
Cool. I can add rows to the grid now. But...
At this point, I've realized I've lost EF change tracking by converting User.UserComments to an ObservableCollection and have no easy way of getting the modifed/new comments back into EF.
So have I approached this all wrong? Is there a better way of updating the related data?
In order for EF to track collection changes, you need to be adding and removing from the collection in the model itself.
this._UserComments = new ObservableCollection<UserComment>(UserData.UserComments);
In the line above, you're creating a collection by copying elements, so when items are added to or removed from UserDetailViewModel.UserComments, those items aren't actually being added to or removed from User.UserComments.
Some options to fix this include:
Changing User.UserComments itself to an ObservableCollection and exposing that in the view model. For example:
public class UserDetailViewModel
{
public virtual User UserData { get; set; }
public ObservableCollection<UserComment> UserComments
{
get { return UserData.UserComments; }
}
// other stuff...
}
Handling add/remove events for UserDetailViewModel.UserComments and modifying the User.UserComments collection there.
This might be helpful as well:
https://msdn.microsoft.com/en-us/data/jj574514.aspx
I have two tables:
public AdminTest()
{
this.AdminTestQuestions = new List<AdminTestQuestion>();
}
public int AdminTestId { get; set; }
public string Title { get; set; }
public virtual ICollection<AdminTestQuestion> AdminTestQuestions { get; set; }
}
public partial class AdminTestQuestion
{
public int AdminTestQuestionId { get; set; }
public int AdminTestId { get; set; }
public System.Guid QuestionUId { get; set; }
public virtual AdminTest AdminTest { get; set; }
}
I am using the following EF6 code to add a new adminTest (with its adminTestQuestions) to the
database:
public async Task<IHttpActionResult> Post([FromBody]AdminTest adminTest)
{
db.AdminTests.Add(adminTest);
foreach (AdminTestQuestion adminTestQuestion in adminTest.AdminTestQuestions)
{
db.AdminTestQuestions.Add(adminTestQuestion);
}
await db.SaveChangesAsync(User, DateTime.UtcNow);
return Ok(adminTest);
}
I have similar but more complicated code to deal with the case where questions are added or removed from the adminTest. All my code works but it would be very good if EF was able to do what I needed rather than my having to add many lines of code.
Can anyone tell me if there have been any changes to EF6 or if any changes are planned to EF7 that will allow it
has noted on the ef7 github they seams to have added some neat code that add primary key entity.
but it is still not clear as to if it will be a common thing for children collection in an entity.
Git hub Entity Framework Design Meeting Notes
but for EF6 you could use a Generic Repository to make all the work for you. (since you can't extend DbContext directly)
assuming db is a DbContext
you could use this -> : Accessing a Collection Through Reflection
then find all Property from a class T that contains ICollection<> and do a foreach on the item of the ICollection Property then do db.Set.Add(proprietyChild) on it
that would eliminate the need for always repeating the same add child to entity code.
some people already did implement a solution thou : Automated updates of a graph of deached entities
I want to create a model on MVC3, but instead of creating a model for a table, I want to create one for a view in my Database. I know that with models for tables, I need to specify primary keys when creating the model, if not i get some error messages. Since views do not have have primary/foreign keys, how do I go about creating models for View?
At any MVC application that works with EF you may have different models to create tables in db or conceptual model for view that doesn't create any tables.Table creation depends on what properties of which model classes you'll define in class that inherited from DbContext. Notice that any property that exists in this class and its relational classes will create table in db. So,to create models that will be used only at view, you can define models that don't have any relationship to other table creator models plus no definition at inherited DbContext class. Then you can use them as model or class to show data at the view.The controller and its related view should use the same model and no errors will be occurred.
public class datacontext : DbContext
{
public DbSet<Person> people { get; set;}
public DbSet<Address> address { get; set;}
}
public class Person
{
[Key]
public int ID { get; set;}
public String Name { get; set;}
}
public class Address
{
[Key]
public int ID { get; set;}
public Country country { get; set;}
public String Details { get; set;}
}
public class Country
{
[Key]
public int ID { get; set;}
public String Name { get; set;}
}
public class Work_Field//without any key definition
{
public String Title { get; set;}
public String Description { get; set;}
}
Here we have this tables : Person, Address, Country(Although it's not defined at datacontext class)
In this example, you can use Work_Field as view model perhaps with some DataAnnotations like [Required],... to force user don't leave parameter empty, and so more.