3

https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew#many-to-many

EF Core 5 has refined the many to many relationship, we don't need to define a Entity for the "relation table", EF Core 5 will internally automatically generated the table.

Then, how can I define the DbSet for referencing to do CRUD?

e.g.

class T1
{
    public string id1{get;set;}
    public virtual ICollection<T2> T2{get;set;}
}
class T2
{
    public string id2{get;set;}
    public virtual ICollection<T1> T1{get;set;}
}

public DbSet<T1> t1{ get; set; }
public DbSet<T2> t2{ get; set; }
  • Note: T1, T2, T1T2 are automatically generated by EF Core in the SQLite.

  • T1T2 class I don't define it actually.

       class T1T2
       {
           public string id1{get;set;}
           public string id2{get;set;}
       }
    

So no

public DbSet<T1T2> t1t2{ get; set; }

While I what to add a record to T1T2, how to get the reference of the Table T1T2? Or do I have any other way to do so?

Progman
  • 16,827
  • 6
  • 33
  • 48
Ming Tong
  • 105
  • 1
  • 8
  • If you need the join table object, you need to define it. To add or remove elements in the jt, you have to remove a t2 entity from the list of t1 (or vice versa) – Isitar Dec 25 '20 at 09:32
  • 1
    You yourself provided a link that shows how to do this. Just copy the code from there. – Alexander Petrov Dec 25 '20 at 09:47
  • @lsitar, I edited the post. t1 has t2 list ref, t2 has t1 list ref. how to add elements in the t3? – Ming Tong Dec 25 '20 at 10:25
  • @Alexander Petrov sorry, I don't find the related code, the code it defined a t3 Entity, I don't think it is expected. – Ming Tong Dec 25 '20 at 10:29
  • @MingTong Please [edit] your question to include the source code of the entity classes and the source code of your context class. – Progman Dec 25 '20 at 10:35
  • @Progman I've updated the post. – Ming Tong Dec 25 '20 at 10:54
  • @MingTong Have you tried using the `OnModelCreating()` method on https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew#code-try-7? What is the result you get? What are the error messages you get? Please [edit] your question to include your attempts on how to add the "link entity" to your context and write the result and/or error messages you get. – Progman Dec 25 '20 at 11:15
  • @Progman I don't get error message. I don't think the example is correct in the link. The code try to ref the relation table by ".UsingEntity", however, the Entity "PostTag" is never defined. – Ming Tong Dec 25 '20 at 11:27
  • @MingTong Have you tried creating the `T1T2` entity, add the `DbSet` and add the code to the `OnModelCreating()` method mentioned in the link? Please [edit] your question to include the result or the error messages you get. – Progman Dec 25 '20 at 11:34
  • @Progman I can create the T1T2, but do we need? EF Core 5 article told us we don't need to create it, but they don't tell us how to ref it. – Ming Tong Dec 25 '20 at 11:39
  • @MingTong You don't need to create it when you don't need it. But you *do* need it (you want to reference/use it), so you have to create it. – Progman Dec 25 '20 at 11:42
  • @Progman Then in which case I don't need it? I mean we should have common requirement to add/remove record to the T1T2, right? then how to do it without define T1T2? – Ming Tong Dec 25 '20 at 11:46
  • @MingTong When you work with the collection navigation properties in `T1` and `T2` *only*, then you don't need the `T1T2` entity itself in your context. EF will handle the mapping to the `T1T2` database table for you, based on the content of the collection navigation properties. – Progman Dec 25 '20 at 11:48
  • @Progman That is not reasonable, If I don't need T1T2 table, then I don't need to define the navigation properties. Anyway, thank you for your explanation, I will try to define T1T2 manually. – Ming Tong Dec 25 '20 at 11:54
  • @MingTong It looks like the feature to use many-to-many without an entity for the join table added June 2020 for EF Core 5.0, see https://github.com/dotnet/efcore/issues/10508. Before that you even have to add an entity to make many-to-many relationships work. – Progman Dec 25 '20 at 11:59
  • @Progman Yes, I know it's a new feature, I just want to try the "what's new". – Ming Tong Dec 25 '20 at 12:06

1 Answers1

2

By default EF Core uses the so called shared property bag entity type for join entity. In simple words, it is Dictionary<string, object> type with unique name, having named "indexer" properties.

It is possible to define/access DbSet for it as soon as you know the name. You do that by using the new Set method overload with name argument:

public DbSet<Dictionary<string, object>> t1t2 => Set<Dictionary<string, object>>("T1T2");

Then you can query or do any operation as with regular typed entity, but dealing with "magic" string property names and types, for instance

context.t1t2.Add(new Dictionary<string, object> { ["T1id1"] = some_id1, ["T2id2"] = some_id2 });

etc.

Shortly, it's possible to work explicitly with the join entity, but it's type unsafe and error prone (similar to working with shadow properties), and more importantly, normally is not needed. Because the operations on join entity (links) - basically add and remove, are normally implemented through manipulating the collection navigation property of one of the entities. For instance, do add link between two existing entities as above, you do something like this

var t1 = context.t1.Find(some_id1);
var t2 = context.t2.Find(some_id2);

and then either

t1.T2.Add(t2);

or

t2.T1.Add(t1);

would do the desired operation (make sure you initialize collection navigation properties of your entities to avoid null reference exceptions, but this is no different than for normal one-to-many relationships).

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • Cool, Can I change the name "T1T2" in the code public DbSet> t1t2 => Set>("T1T2"); It just join the two table name, I want to redefine it. – Ming Tong Dec 25 '20 at 12:34
  • This is all default (with your entity/property names). If you need to change something, you have to use one of the `UsingEntity` luent API overloads. Examples here https://stackoverflow.com/questions/64919574/change-name-of-generated-join-table-many-to-many-ef-core-5/64920457#64920457 and here https://stackoverflow.com/questions/64345107/seeding-many-to-many-databases-in-efcore5-with-modelbuilder/64349955#64349955 – Ivan Stoev Dec 25 '20 at 12:56
  • when I try to add data, it showed Unable to track an instance of type 'T1T2 (Dictionary)' because it does not have a primary key. Only entity types with primary keys may be tracked. Is it reasonable to have a primary key for a T1T2 table? – Ming Tong Dec 25 '20 at 13:10
  • After I add a "id" as a primary key, it then showed The instance of entity type 'T2' cannot be tracked because another instance with the same key value for {'id2'} is already being tracked. – Ming Tong Dec 25 '20 at 13:10
  • (1) There is no need to define PK because EF Core already should have defined one composite PK containing the 2 FKs. Again, the approach with working directly with the implicit join entity is error prone because you need to know exactly the EF Core conventions line entity/property names and types. I've explained them in one of the links, so repeating it here makes no sense. Every SO post is for a single concrete issue. – Ivan Stoev Dec 25 '20 at 13:41
  • The link also contains the alternative - explicitly naming everything as well as mapping concrete explicit entity (and still getting skip navigation benefits) which is tradeoff eliminating the drawbacks of using unsafe strings and dictionaries. – Ivan Stoev Dec 25 '20 at 13:42