简体   繁体   中英

Issue with an EF Core query

I am trying to create a query for an entity that has a one-to-many relationship to another entity in EF Core .

Here are the two entities:

public class Computer {
  public int ComputerId { get; set; }
  public string Hostname { get; set; }
  public string IpAddress { get; set; }

  public ICollection<Event> Events { get; set; }
}

public class Event {
  public int EventId { get; set; }
  public string Description { get; set; }
  public DateTime EventTime { get; set; }

  public Computer Computer { get; set; }
}

I am trying to find out how I would go about querying all Computer records with the EventId of the newest Event .

I understand how to do simple queries on each, how would I go about writing a query that would return the data I need from both?

Newer versions of EF Core (5+) understand how to turn this into a ROW_NUMBER OVER, which is an efficient way to get what you're after:

context.Computers
  .Select(c => new {
    Computer = c, //or just take some properties from it
    RecentEvent = c.Events.OrderByDescending(e => e.EventTime).FirstOrDefault()
  });

It gets written to something like:

SELECT *
FROM
  Computers c
  INNER JOIN (
    SELECT *, ROW_NUMBER() OVER(PARTITION BY ComputerId ORDER BY EventTime DESC) rn
    FROM Events
  ) e ON c.ComputerId = e.ComputerId AND rn = 1

It's the "and rn=1" that does the magic


This gets the whole of each object, but you can select just the ID if you want (I assumed you wanted more data from the event).

Off the top of my head, you probably want something close to this. I might have my clauses ordered wrong, but I always need to monkey around with EF Core queries.

using var ctx = new MyDatabaseContext();

var newestEventsForEachComputer =
    ctx.Events
       .AsNoTracking()
       .Include(e => e.Computer)
       .OrderByDescending(e => e.EventTime)
       .GroupBy(e => e.Computer.Id)
       .Select(g => g.First())
       .ToList();

Note: I am assuming it is valid to have the .Include clause there given your description of the table linking.

You can try this, the result will be a list of Computers and they will have a list with only one Event with the latest id

dbContext.Computer.Include(x => x.Event.OrderByDescending(x => x.Id).Take(1)).Select(x => x).ToList();

First of all, you need to build a proper relationship between models. If you go with the code first approach. follow the below code snippets

 public class Computer
{
    [Key]
    public int ComputerId { get; set; }
    public string Hostname { get; set; }
    public string IpAddress { get; set; }

    [InverseProperty("Computer")]
    public ICollection<Event> Events { get; set; }
}


public class Event
{
    [Key]
    public int EventId { get; set; }
    public string Description { get; set; }
    public DateTime EventTime { get; set; }
    public Computer Computer { get; set; }
}

Now you have created a good relationship between the computer and the event model.

Now, the computer entity has a collection of its own events and those events has the same computer entity.

But you cannot get collection of computers just by querying a single eventId. Because your relationship is not like that. You can query like this

 [HttpGet]
    public async Task<IActionResult> GetByIdAsync(int id)
    {
        var result = await _context.Computers.Include(a => a.Events).FirstOrDefaultAsync(a => a.ComputerId == id);
        return new JsonResult(result);
    }

This will return a specific computer with related events.

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