1

This is a trimmed down example of the basic schema I'm working with. I realize this example is convoluted, but there are reasons that this schema is in use which are unimportant to describing the problem I'm seeing.

Also worth noting: I am using Database First.

Team (Table)
Id
Name

Player (Table)
Id
TeamId
Year
Name
Position

Game (Table)
Id
Date
HomeTeamId
AwayTeamId

GameHomePlayer_VW (View)
GameId (This is defined as the PK in the EDMX)
HomePlayerId

The View GameHomePlayer_VW is defined as:

SELECT
    Game.Id,
    Player.Id
FROM Game
LEFT OUTER JOIN Player
ON
    Game.HomeTeamId = Player.TeamId AND
    YEAR(Game.Date) = Player.Year

(If you're curious there is also a GameAwayPlayer_VW :))

EDMX Association between Game and GameHomePlayer_VW:

Referential Constraint      Game -> GameHomePlayer_VW

End1 Multiplicity           0..1 (Zero or One of GameHomePlayer_VW)
End1 Navigation Property    Game
End1 OnDelete               None
End1 Role Name              GameHomePlayer_VW
End2 Multiplicity           1 (One of Game)
End2 Navigation Property    GameHomePlayer_VW
End2 OnDelete               None
End2 Role Name              Game
Name                        GameHomePlayer_VWGame

Game and GameHomePlayer_VW classes generated by EF:

public partial class Game
{
    public int Id { get; set; }
    public DateTime Date { get; set; }
    public Nullable<int> HomeTeamId { get; set; }
    public Nullable<int> AwayTeamId { get; set; }

    public virtual Team HomeTeam { get; set; }
    public virtual Team AwayTeam { get; set; }
    public virtual GameHomePlayer_VW GameHomePlayer_VW { get; set; }
    public virtual GameAwayPlayer_VW GameAwayPlayer_VW { get; set; }
}

public partial class GameHomePlayer_VW
{
    public int GameId { get; set; }
    public Nullable<int> PlayerId { get; set; }

    public virtual Game Game { get; set; }
    public virtual Player Player { get; set; }
}

This setup allows me to query through the view objects as so:

private async Task<Game> GetGame(int id)
{
    return await Context.Games
        .Include(i => i.GameHomePlayer_VW.Player)
        .Include(i => i.GameAwayPlayer_VW.Player)
        .FirstOrDefaultAsync(i => i.Id == id);
}

The code I'm using to delete the game is as follows:

private async Task DeleteGame(int id)
{
    var game = await Context.Games.FirstOrDefaultAsync(i => i.Id == id);
    Context.Games.Remove(game);
    await Context.SaveChangesAsync();
}

The problem I'm running into is that when I try to delete a Game entity using EF I receive the following error:

Unable to update the EntitySet 'GameHomePlayer_VW' because it has a DefiningQuery and no <DeleteFunction> element exists in the <ModificationFunctionMapping> element to support the current operation.

There are other entities that reference game and as using cascade deletes to be removed.

I tried unsuccessfully to use the solution in this post: Unable to update the EntitySet - because it has a DefiningQuery and no <UpdateFunction> element exist

I also tried wiring a stored proc that took the PK as defined by the EDMX and returned 0.

I tried initializing a new instance of my DbContext class to delete it like below and still run into the same problem.

private async Task DeleteGame(int id)
{
    using (var context = new MyDbContext)
    {
        var game = await context.Games.FirstOrDefaultAsync(i => i.Id == id);
        context.Games.Remove(game);
        await context.SaveChangesAsync();
    }
}

Does anyone have any suggestions on maintaining a dynamic link based on the Game's Date while allowing the entities that reference it to be Added/Updated/Deleted?

Matt
  • 77
  • 6
  • 1
    How are you trying to delete a game? You should show the code where this happens. Seemingly you're trying to use the view for that, but why? – Gert Arnold May 22 '18 at 19:23
  • Gert, thanks for the tip. I realize now that without documenting the code for the delete I'm leaving out a big piece of the puzzle! – Matt May 22 '18 at 19:38
  • 1
    This makes me suspect that `Context`'s life cycle is too long. If so, when you get the `Game` to delete it, the previously loaded `...._VW` entities are still attached to it ans also marked for deletion. – Gert Arnold May 22 '18 at 20:41
  • I tried instantiating a new instance of my DbContext class to delete the game and still see the same problem. The `Context` variable is being injected into the Controller I'm using for each API request. Because of this, I wasn't expecting a new instance to solve the problem, but your hunch resonated with me. – Matt May 23 '18 at 14:06
  • OK, the plot thickens... When EF deletes an entity it also tries to cut any associations it has. In this case this could mean that EF wants to update the view entities. It would help to see the actual classes' code to see how the associations work. – Gert Arnold May 23 '18 at 14:25
  • I added additional info about the EDMX association and generated classes. At this point I wonder if the `LEFT OUTER JOIN` used in the View should become an `INNER JOIN`. That may help in a case where the `HomeTeamId` or `AwayTeamId` is `NULL` on the `Game`, but I wonder if I'll still run into the same case if it's not `NULL`. – Matt May 23 '18 at 16:04
  • I haven't got much time to look into this, sadly, but you should check what's in `context.ChangeTracker.Entries` before `SaveChanges`. Also, I'm a bit puzzled about the 1:0..1 associations. That implies that a `Game` has only one `Player` in each view. What's the role of these players? – Gert Arnold May 24 '18 at 06:41
  • The `ChangeTracker` does have related entities despite the freshly initialized `MyDbContext`, two of which are those views. The Game/Player analogy I'm using isn't an exact representation of the schema I'm working with. I think my response to that is pretend it's a really weird team sport where each team only has one player per year, lol. I'm debating removing the related entities from the context before saving, but my gut feeling is that could cause other unintended side effects in larger server side transactions. – Matt May 24 '18 at 15:42
  • I think at this point I plan to turn the Views into actual tables and write Insert/Update/Delete triggers for their related tables to manage the mappings. If that's successful I'll report back with that as the answer. – Matt May 24 '18 at 15:45

1 Answers1

0

I ended up solving this by creating Pure Join Tables for each of the entities that needed mapping and writing triggers on the Game table to Insert, Update, or Delete records in the mapping tables based on the logic that was previously in the views.

EF was much happier Add/Edit/Delete the entities that were attached to them after this.

GameHomePlayerMap (Table)
GameId (PK, FK to Game)
HomePlayerId (FK to Player)

GameAwayPlayerMap (Table)
GameId (PK, FK to Game)
AwayPlayerId (FK to Player)

Matt
  • 77
  • 6