简体   繁体   中英

Entity Framework Related Data Foreign Keys

I have an entity that consists only of foreign keys of other Entities. My simplified class looks like this:

 DeliverNote
     Adress add1 {get; set;}
     Adress add2 {get; set;} 

I can load adresses by themselves just fine, but I can't load a DeliveryNote, because EF doesn't load the related data by default, I think. So I saw solutions, mainly with context.notes.Include(dn => dn.Adresses), but I just can't figure out how I tell the note or the adress class how they're related to each other. Basically when I type "dn." nothing shows up.

The simplest, probably working, solution I saw was from microsoft. In the github from this page https://docs.microsoft.com/de-de/ef/core/querying/related-data you can see the Blog and the Post classes. To me the Post class looks flawed though, why would a Post have to know about the Blog it is in? This will mess up the database too in code first solutions. What if the same post is gonna be posted in several blogs?

Most solutions also seem to be lists of some kind, I don't have a list, just simple single objects. 1-1 relationship, I think.

If you define your model as:

public class DeliverNote {
    public int Id { get; set; }
    public Adress addr1 { get; set; }
    public Adress addr2 { get; set; }
}

public class Adress {
    public int Id { get; set; }
}

You can then call:

context.notes.Include(dn => dn.addr1).Include(dn => dn.addr2);

Which will include the related data.

Your model doesn't define foreign keys for addr1 or addr2 so EF Core will create shadow properties for you, ie columns that exist in the table but not as properties in the c# model.

So you have a database with a table of Addresses and a table of DeliveryNotes . Every DeliveryNote has two foreign keys to the Addresses : one From and one To (you call it addr1 and addr2)

If you follow the entity framework code first conventions , you'll have something like this:

class Address
{
     public int Id {get; set;}
     ... // other properties

     // every Address has sent zero or more delivery Notes (one-to-many)
     public virtual ICollection<DeliveryNote> SentNotes {get; set};

     // every Address has received zero or more delivery Notes (one-to-many)
     public virtual ICollection<DeliveryNote> ReceivedNotes {get; set};
}

class DeliveryNote
{
     public int Id {get; set;}
     ... // other properties

     // every DeliveryNote comes from an Address, using foreign key
     public int FromId {get; set;}
     public virtual Address FromAddress {get; set;}

     // every DeliverNote is sent to an Address, using foreign key:
     public int ToId {get; set;}
     public virtual Address ToAddress {get; set;}
}

In entity framework the columns of the tables are represented by non-virtual properties. The virtual properties represent the relations between the tables.

Note that the ICollection and FromAddress / ToAddress are virtual and thus not columns into your columns. If desired you can leave them out of your classes. However, if you have these virtual properties, you don't have to do the (Group)Joins yourself.

I can load adresses by themselves just fine, but I can't load a DeliveryNote, because EF doesn't load the related data by default ... I

From this it is not easy to detect what kind of queries you want.

One of the slower parts of database queries is the transport of the selected data from your DBMS to your local process. Hence it is wise to minimize the data being transported.

If you use Include , then the complete object is transported, inclusive the foreign keys and all properties you don't need. If you have a database with Schools and Students, then every Student will have a foreign key to the School he attends. If you ask for a 'School with his 1000 Students' of school with Id 4, using Include, you don't want to transport the foreign key SchoolId a 1000 times, because you already know it will have value 4

In entity framework only use Include if you want to change / update the fetched item, otherwise use Select

Given a bunch of DeliveryNotes, give me some AddressDetails of it:

IQueryable<DeliveryNote> deliveryNotes = dbContext.DeliveryNotes
   .Where (deliveryNote => ...) // probably something with Id, or Date, or subject
   .Select(deliveryNote => new
   {
       // select only the delivery note properties you actually plan to use
       Subject = deliveryNote.Subject,
       DeliveryDate = deliveryNote.DeliveryDate,
       ...

       From = new
       {
           // select only the From properties you plan to use
           Id = deliveryNote.FromAddress.Id,
           Name = deliveryNote.FromAddress.Name,
           Address = deliveryNote.FromAddress.Address,
           ...
       }

       To = new
       {
            // again: only properties you'll use
            Name = deliveryNote.ToAddress.Name,
            ...
       },
   });

Entity framework knows the one-to-many relationship and will perform the proper join for you.

Given a bunch of Addresses give me some of the DeliveryNotes they received

var query = dbContext.Addresses
    .Where(address => address.City == "New York" && ...)
    .Select(address => new
    {
         // only properties you plan to use
         Id = address.Id,
         Name = address.Name,

         ReceivedNotes = address.ReceivedNotes
             .Where(note => note.DeliveryDate.Year == 2018)
             .Select(note => new
             {
                 // only properties you plan to use:
                 Title = note.Title,
                 ...

                 // no need for this, you know it equals Id
                 // AddressId = note.FromId,
             }),
    });

Entity framework knows the one-to-many relationship and will do the proper groupjoin for you.

If you have a one-to-many relationship and you want the "item with its many sub-items", start on the one-side and use the virtual ICollection. If you want the sub-item with the item that it belongs to, start with the many-side and use the virtual property to the one-side

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM