So Many-to-Many did not make it into .NET Core 3.0 release, bummer...
I'm aware of how to implement m:m using a join entity as in this example: https://stackoverflow.com/a/53972658/980917
My question is about the model classes themselves. Consider Student & Class example:
Student - has ICollection<StudentClass>
StudentClass - joining entity
Class - has ICollection<StudentClass>
That is fine for data loading purposes. But in your business logic collection of StudentClasses is not useful as its just a joining entity with 2 IDs. When working with Student or Class you actually want an actual collection of Classes inside a Student and a collection of Students inside a Class. (ie: Student.Classes & Class.Students.
What's the current recommended approach / workaround to retrieve the many-to-many collections (not joining entity)?
Do we have to do a 2nd select based on on the joining entity or is there something more elegant?
A quick example or a link would be great. Thanks.
What's the current recommended approach / workaround to retrieve the many-to-many collections (not joining entity)?
You can do easily with .Include extension method as follows:
Lets say your Student class as follows:
public class Student
{
public int Id {get; set;}
public string StudentName {get; set;}
public ICollection<StudentClass> StudentClasses {get; set;}
}
To retrieve all the students with their associated classes:
var studentsWithClasses = _context.Students.Include(s => s.StudentClasses).ToList();
To retrieve a single student with its classes:
var studentWithClasses = _context.Students.Where(s => s.Id = studentId).Include(s => s.StudentClasses).FirstOrDefault();
Related
Consider this scenario:
Table1
Field1
Field2
Field3
We are writing an ERP application and it is common for implementations have additional fields based on user needs say User1 and User2 to Table1. Information about these user fields is stored in metadata tables. Custom fields will differ from one implementation to other.
I am wondering if EF can support this requirement.
Ideally I would like to have model classes generated like
partial class Table1
public Field1 {get; set;}
public Field2 {get; set;}
public Field3 {get; set;}
///The user fields collection should store user defined fields
public List<string, object) UserFields {get; set;}
When I say context.Table1.Get("foo"), system should return appropriate data
Is this possible in EF?
regards,
Abhishek
I could not find relevant examples. I can modify template files so that all entities can have additional property public List<string, object) UserFields {get; set;}. But I have no idea how EF can recognize additional user fields and hydrate entities accordingly. Any help will be greatly appreciated.
I am designing a data entry application using EntityFramework (Code First) to collect customers detail.
The data structure required is simple.
Customer entity has some flat and one-to-many details (eg. Name, Telephone Numbers, etc.) and then a large number of many-to-many properties which always follow the same pattern which allow for multiple choice from a list (in the UI, this would be shown as a checkbox list), which the user can also add items to. For each of these multiple choice properties, there is also one Notes property that allow the user to explain why these details where connected to the customer (in other words, this is just a string within the Customer entity).
Because of the similarity of these properties and the relative simplicity of the data, I started looking to model using inheritance however I am now thinking that perhaps there are better ways to achieve this, especially because there would be a major benefit if the system allowed an admin user to add a new property of this type dynamically.
I am looking for any suggestions to achieve this without having to define and connect all the entities manually or at least to minimize the amount of code required to do so.
SQL does not know the concept of inheritance. However there are several strategies to let entity framework accept your inherited classes. Which strategy you ought to use depends on the type of queries you will ask most.
Suppose you have two classes Student and Teacher. Both classes have a lot of properties (and possibly methods) in common. You'd like to put them in a base class: Person. You don't want to be able to create a Person object, so your Person class will be abstract.
In C#:
abstract class Person
{
... // common person properties
}
class Teacher : Person
{
public int Id {get; set;} // Primary Key
... // Teacher properties
}
class Student : Person
{
public int Id {get; set;} // Primary Key
... // Student properties
}
You are not planning to create Person objects, only Teachers and Students. Therefore you could create a Teachers table and a Students table. The Teachers table contains all Teacher properties plus all Person properties. Similarly a Student table contains the Student properties and all Person properties. For every concrete (= non-abstract) class you create a table.
This strategy is called Table-Per-Concrete-Class (TPC). It is very similar to a composition: a Teacher 'has' Person properties, instead of inherits Person properties. It follows the old adagium "Favour composition over inheritance"
You inform entity framework that you want this strategy in your DbContext
class MyDbContext : DbContext
{
public DbSet<Student> Students {get; set;}
public DbSet<Teacher> Teachers {get; set;}
// we don't want a Person table, so no DbSet<Person>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Tell entity framework that the Teacher Table will include Person properties
modelBuilder.Entity<Teacher>().Map(m =>
{
m.MapInheritedProperties();
});
// Tell entity framework that the Student table will include Person properties
modelBuilder.Entity<Student>().Map(m =>
{
m.MapInheritedProperties();
});
}
}
A query for "Give me all Teachers who ..." or "Give me the Students that ..." will involve one table. However, if you ask: "Give me all Persons that ..." will require a concat of two tables.
var result = myDbContext.Teachers.Cast<Person>()
.Concat(myDbContext.Students.Cast<Person>())
.Where(person => ... // something with Person properties)
.Select(person => ... // something with Person properties);
Whenever I need to model inheritence, I use this TPC strategy most of the time.
If you think that you'll be querying quite often for Persons that ... instead of Teachers who ..., consider using Table Per Type (TPT)
In TPT you'll have three tables: a Person table, containing all the Person properties, a Teacher table with the Teacher properties and a foreign key to the Person properties of this Teacher. Similarly you'll have a Student table with a foreign key to the Person properties it inherits.
Asking for "all Persons that ..." will only involve one table, whether the Person is a Student or a Teacher. Because you ask for Persons, you don't want any Student properties.
Asking for "all Teachers that ..." will always involve two tables, namely the Teacher table to get the Teacher properties and the Person table to access the Person properties.
So if you ask more often for "Persons who ..." that for "Teachers who ...", consider using TPT.
I'm using Entity Framework - Database First in my project. My model has a view with only one VARCHAR column:
CREATE VIEW MyView
AS
SELECT 'Eris' Eris
FROM MyTable
By default, this view gets mapped to its own entity with just one property:
public virtual DbSet<MyView> MyViews { get; set; }
How can I change this so that the view gets mapped to a List of strings instead:
public virtual List<string> Eris { get; set; }
Unfortunately EF does not support mapping collections of value types. If you really want to implement this scenario then you might want to look into other ORM frameworks that have this feature like NHibernate.
If that's not an option and you have to stick to EF then you're forced to create an entity with one property like you mentioned in your question.
The entity model represents one element in the table.
When you retrieve data from the table, you will get a list of entity model objects.
I have two entities: Recipe and Ingredient.
Entites:
public class Ingredient
{
public int IngredientId{get;set;}
public string Name {get;set;}
public virtual ICollection<Recipe> Recipies {get;set;}
}
public class Recipe
{
public int RecipeId{get;set;}
public string Name {get;set;}
public virtual ICollection<Ingredient> Ingredients {get;set;}
}
My two entities map to their respective tables in the "Web" schema in our database:
ToTable( "Recipe", "Web" );
ToTable( "Ingredient", "Web" );
... and everything works fine. The only hiccup is that the generated many-to-many table is created in the "dbo" schema.
dbo.RecipeIngredients
Without defining the relationships in the Fluent API, is there a way to specify "Web" as the table schema to use for the many-to-many tables?
No. Data annotations support only basic subset of mapping features. If you want to have full code first mapping feature set you must use Fluent API (which is also much cleaner way to define mapping). Defining anything related to junction table is considered as advanced mapping feature and it is currently available only in Fluent API.
I have the following
[DataContractAttribute]
public class Animal
{
[Key]
[XmlElement(ElementName = "Id")]
[DataMember()]
public Guid Id
{
get;
set;
}
[XmlElement(ElementName = "AnimalType")]
[DataMember()]
public List<AnimalType> AnimalType
{
get;
set;
}
}
And i map it through the code first approach with EF to tables
modelBuilder.Entity<Animal>().ToTable("Animal");
As you see I have not performed some complex mapping, but the List of AnimalType enumerations did not get mapped automatically to any columns/tables in the DB. Do i need to add some extra code to the model builder to control the mapping of an enumeration list ?
As of EF CTP5, enums are not supported yet. The team announced that they are going to fully support enums in their next RTM version which is targeted to be released on the first quarter of 2011.
I know for the longest time, enums weren't supported by EF, though I don't know if that is still the case or not.
Either way, I think there is a general problem with having EF handle a list of a type other than another entity. What is the primary key? What is the value? Should it try to store the data in one column or create a separate table and create a foreign key constraint? These are questions that will likely need to be answered before your model can be converted into a database schema.