C
C#7d ago
Qvabbyte

entity framework help needed pls.

So I have this method which is the main logic of Spinning mechanism. And well those are the main models where I have issue, Issue is that when I assign dateEnded to session which means that this certain session is ended right, in this spin logic in one of t he first statement it checks that, if the session is ended it creates new session which I want to be saved in the database. and I am 100% sure I assign playerId to it but well the error says it can't be assigned the null and doesn't saves Here is part of debugging pics too and it txt file there is spin method.
86 Replies
Qvabbyte
QvabbyteOP7d ago
db context
Qvabbyte
QvabbyteOP7d ago
player session
Qvabbyte
QvabbyteOP7d ago
c#
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace DeepDiveSlots.Models
{
/// <summary>
/// Represents the state of the game.
/// </summary>
public class GameState
{
private GameState() { }
[Key]
public Guid Id { get; set; } = Guid.NewGuid();

//public string SessionId { get; } = Guid.NewGuid().ToString();
[Column(TypeName = "decimal(18,2)")]
public decimal CurrentBalance { get; set; } = 1000.00m;
[Column(TypeName = "decimal(18,2)")]
public decimal TotalWagered { get; set; } = 0.00m;
[Column(TypeName = "decimal(18,2)")]
public decimal TotalWon { get; set; } = 0.00m;

public DateTime SessionStart { get; } = DateTime.UtcNow;
public DateTime LastActivity { get; set; } = DateTime.UtcNow;

[Column(TypeName = "decimal(18,2)")]
public decimal CurrentBet { get; set; } = 0.20m; // default bet amount
public string Currency { get; set; } = "GEL"; //default currency

//Foreign Key
public Guid PlayerSessionId { get; set; }

public GameState(decimal startingBalance = 1000.00m)
{
try
{
if (startingBalance < 0)
{
throw new ArgumentException("Balance cannot be negative");
}
CurrentBalance = startingBalance;
}
catch (Exception e)
{
Console.WriteLine($"GAMESTATE ERROR: {e.Message}");
}
}
}
}
c#
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace DeepDiveSlots.Models
{
/// <summary>
/// Represents the state of the game.
/// </summary>
public class GameState
{
private GameState() { }
[Key]
public Guid Id { get; set; } = Guid.NewGuid();

//public string SessionId { get; } = Guid.NewGuid().ToString();
[Column(TypeName = "decimal(18,2)")]
public decimal CurrentBalance { get; set; } = 1000.00m;
[Column(TypeName = "decimal(18,2)")]
public decimal TotalWagered { get; set; } = 0.00m;
[Column(TypeName = "decimal(18,2)")]
public decimal TotalWon { get; set; } = 0.00m;

public DateTime SessionStart { get; } = DateTime.UtcNow;
public DateTime LastActivity { get; set; } = DateTime.UtcNow;

[Column(TypeName = "decimal(18,2)")]
public decimal CurrentBet { get; set; } = 0.20m; // default bet amount
public string Currency { get; set; } = "GEL"; //default currency

//Foreign Key
public Guid PlayerSessionId { get; set; }

public GameState(decimal startingBalance = 1000.00m)
{
try
{
if (startingBalance < 0)
{
throw new ArgumentException("Balance cannot be negative");
}
CurrentBalance = startingBalance;
}
catch (Exception e)
{
Console.WriteLine($"GAMESTATE ERROR: {e.Message}");
}
}
}
}
In the first Image you can clearly see that PlayerId is assigned - 1 however error says otherwise It could be that its' referencing to different thing but I dont know .... I tried saving new session first but when I save it errors on that save now :( If anyone needs me to provide anything else please tell me I wanna fix this asap If anyone responds pls ping me
Ꜳåąɐȁặⱥᴀᴬ
there is no csproj but i imagine you have nullable enabled
Qvabbyte
QvabbyteOP7d ago
oh yeas I have nullable enabled that could be the problem?
Ꜳåąɐȁặⱥᴀᴬ
hmm im still reading tbh but considering field is nullable and a struct i guess it's not a problem
Qvabbyte
QvabbyteOP7d ago
yeah didn't change a thing when I disabled. This is the InnerException btw : SqlException: Cannot insert the value NULL into column 'PlayerId', table 'DeepDiveSlots.dbo.PlayerSessions'; column does not allow nulls. UPDATE fails.
Ꜳåąɐȁặⱥᴀᴬ
pardon, i was looking at the end date and not playerId which btw why is a string
Qvabbyte
QvabbyteOP7d ago
well it will take guid most likely that's why im assuming I have foreign keys and relations messed up
Ꜳåąɐȁặⱥᴀᴬ
technically this
var newSession = new PlayerSession
{
PlayerId = player.Id,
var newSession = new PlayerSession
{
PlayerId = player.Id,
could also be
var newSession = new PlayerSession
{
PlayerId = playerId,
var newSession = new PlayerSession
{
PlayerId = playerId,
since there's already the variable but did you debug this step by step? did you see if fields are filled correctly?
Qvabbyte
QvabbyteOP7d ago
well true but either way it still takes same id yes I did if you check the first image which is taken in debugging mode all the non-nullables are filled Especially that "PlayerId" is assigned to player too
Ꜳåąɐȁặⱥᴀᴬ
in theory yes, but in practice code is not working so you can't be sure do you have the generated sql in the logs? is it really null?
Qvabbyte
QvabbyteOP7d ago
I don't have logs yet how can I do that tho? okay wait I will check sql query string now
"DECLARE @__playerId_0 nvarchar(450) = N'1';\r\n\r\nSELECT [p].[Id], [p].[Balance], [p].[LifeTimeWagered], [p].[LifeTimeWinnings], [p0].[Id], [p0].[CreatedAt], [p0].[DeviceHash], [p0].[EndedAt], [p0].[IpAddress], [p0].[PlayerId], [p0].[ActiveBonus_BonusType], [p0].[ActiveBonus_RemainingSpins], [g].[Id], [g].[Currency], [g].[CurrentBalance], [g].[CurrentBet], [g].[LastActivity], [g].[PlayerSessionId], [g].[TotalWagered], [g].[TotalWon]\r\nFROM [Players] AS [p]\r\nLEFT JOIN [PlayerSessions] AS [p0] ON [p].[Id] = [p0].[PlayerId]\r\nLEFT JOIN [GameStates] AS [g] ON [p0].[Id] = [g].[PlayerSessionId]\r\nWHERE [p].[Id] = @__playerId_0"
"DECLARE @__playerId_0 nvarchar(450) = N'1';\r\n\r\nSELECT [p].[Id], [p].[Balance], [p].[LifeTimeWagered], [p].[LifeTimeWinnings], [p0].[Id], [p0].[CreatedAt], [p0].[DeviceHash], [p0].[EndedAt], [p0].[IpAddress], [p0].[PlayerId], [p0].[ActiveBonus_BonusType], [p0].[ActiveBonus_RemainingSpins], [g].[Id], [g].[Currency], [g].[CurrentBalance], [g].[CurrentBet], [g].[LastActivity], [g].[PlayerSessionId], [g].[TotalWagered], [g].[TotalWon]\r\nFROM [Players] AS [p]\r\nLEFT JOIN [PlayerSessions] AS [p0] ON [p].[Id] = [p0].[PlayerId]\r\nLEFT JOIN [GameStates] AS [g] ON [p0].[Id] = [g].[PlayerSessionId]\r\nWHERE [p].[Id] = @__playerId_0"
c#
var query =_dbContext.Players.Include(p => p.ActiveSession)
.ThenInclude(s => s.GameState)
.Where(p => p.Id == playerId);
var sql = query.ToQueryString();
c#
var query =_dbContext.Players.Include(p => p.ActiveSession)
.ThenInclude(s => s.GameState)
.Where(p => p.Id == playerId);
var sql = query.ToQueryString();
Ꜳåąɐȁặⱥᴀᴬ
eh but you need the update query, not the select one
Qvabbyte
QvabbyteOP7d ago
No description
Qvabbyte
QvabbyteOP7d ago
This is where it errors yeah but doesn't ef generate that itself?
Ꜳåąɐȁặⱥᴀᴬ
the driver writes the query but maybe this is erroring before getting there i don't know doesn't seem like an sql server exception no it should be from the server, weird
Qvabbyte
QvabbyteOP7d ago
I also get this error:
SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_GameStates_PlayerSessions_PlayerSessionId". The conflict occurred in database "DeepDiveSlots", table "dbo.PlayerSessions", column 'Id'.
Cannot insert the value NULL into column 'PlayerId', table 'DeepDiveSlots.dbo.PlayerSessions'; column does not allow nulls. UPDATE fails.
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_SpinRecords_PlayerSessions_PlayerSessionId". The conflict occurred in database "DeepDiveSlots", table "dbo.PlayerSessions", column 'Id'.
The statement has been terminated.
The statement has been terminated.
The statement has been terminated.
SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_GameStates_PlayerSessions_PlayerSessionId". The conflict occurred in database "DeepDiveSlots", table "dbo.PlayerSessions", column 'Id'.
Cannot insert the value NULL into column 'PlayerId', table 'DeepDiveSlots.dbo.PlayerSessions'; column does not allow nulls. UPDATE fails.
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_SpinRecords_PlayerSessions_PlayerSessionId". The conflict occurred in database "DeepDiveSlots", table "dbo.PlayerSessions", column 'Id'.
The statement has been terminated.
The statement has been terminated.
The statement has been terminated.
Ꜳåąɐȁặⱥᴀᴬ
just to be sure, is your db in sync with the models?
Qvabbyte
QvabbyteOP7d ago
migration u mean?
Qvabbyte
QvabbyteOP7d ago
wait let me check it must be tho yeah it must be I added another migration and updated db just in case still same error
Ꜳåąɐȁặⱥᴀᴬ
(ps i would use nameof() instead of magic string in attributes like [ForeignKey("Player")])
Qvabbyte
QvabbyteOP7d ago
yeah thanks for reminding that, this is newly started project so when I write simple logic I then rewrite it more optimized
Ꜳåąɐȁặⱥᴀᴬ
anyway this is kinda weird, i would still try to recover insert/update sql to look if [session]id/playerId are what they should be
Qvabbyte
QvabbyteOP7d ago
in that case before adding EndSession and starting new session logic It has saved last session in database. so logically when I run the code player still have old session attached but its' isActive is false that's why it goes inside that if else statement and tries to attach new playerSession to it but in old session data playerId and everything is fine
Qvabbyte
QvabbyteOP7d ago
No description
Qvabbyte
QvabbyteOP7d ago
Player HasOne playerSession is right logic too player can't be playing two different sessions so I don't think anything is wrong with model builder either should I clean project or something?
Ꜳåąɐȁặⱥᴀᴬ
oh wait it's dinner time for me but this i bet could be a return value instead of an exception
if (player.Balance < betAmount)
throw new InvalidOperationException("Insufficient balance");
if (player.Balance < betAmount)
throw new InvalidOperationException("Insufficient balance");
and you shouldn't be assigning Player here
var newSession = new PlayerSession
{
PlayerId = player.Id,
GameState = new GameState { CurrentBalance = player.Balance },
Player = player // no
};
var newSession = new PlayerSession
{
PlayerId = player.Id,
GameState = new GameState { CurrentBalance = player.Balance },
Player = player // no
};
only playerId
Qvabbyte
QvabbyteOP7d ago
oh yeah logical but I think I had that way before error too haha wait lemme test thanks for trying to help tho however this wasn't solution to it
leowest
leowest7d ago
can you re-$paste your code to the site below... discord is awful to read when it embeds it
MODiX
MODiX7d ago
If your code is too long, you can post to https://paste.mod.gg/, save, and copy the link into chat for others to see your shared code!
Qvabbyte
QvabbyteOP7d ago
yeah sure wait
leowest
leowest7d ago
you can create tabs at the top to separate pages and do it all in link also what db engine are u using sql? sqlite? postgresql? something else?
Qvabbyte
QvabbyteOP7d ago
BlazeBin - wjhqvmrzncmz
A tool for sharing your source code with the world!
Qvabbyte
QvabbyteOP7d ago
SQL oh wait I didnt read about tabs
leowest
leowest7d ago
MS SQL I meant to say
Qvabbyte
QvabbyteOP7d ago
wait I will seperate
leowest
leowest7d ago
yeah there is a + at the top you can add multiple "files" per say
Qvabbyte
QvabbyteOP7d ago
yes
Qvabbyte
QvabbyteOP7d ago
BlazeBin - xdzjaajpyybz
A tool for sharing your source code with the world!
Qvabbyte
QvabbyteOP7d ago
here with tabs
leowest
leowest7d ago
so first thing I see wrong is
[Key]
public string Id { get; set; } = Guid.NewGuid().ToString();
[Key]
public string Id { get; set; } = Guid.NewGuid().ToString();
Qvabbyte
QvabbyteOP7d ago
in SpinAsync Method where I get the error you can see lots of comments where I try lots of different things, saving stuff before stuff and etc.
leowest
leowest7d ago
its not the code's responsibility to generate the ID AFAIK its handled by the database
Qvabbyte
QvabbyteOP7d ago
Yeah I know that I don't handle creating/updating/deleting players currently tho
leowest
leowest7d ago
so just
public Guid Id {get;set;}
public Guid Id {get;set;}
or
public int Id {get;set;}
public int Id {get;set;}
either will work u dont need to specify Key either its implied by efc
Qvabbyte
QvabbyteOP7d ago
Okay I changed that
leowest
leowest7d ago
you also dont need to define these in both code and fluent api its also implied [ForeignKey(nameof(Player))]
Qvabbyte
QvabbyteOP7d ago
Soo that is unnecessary on top?
leowest
leowest7d ago
ideally you just need to do for example
public class Player
{
public int Id {get;set;}

public int PlayerSessionId {get;set;}
public PlayerSession Session {get;set;}
}

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

public int PlayerId {get;set;}
public Player Player {get;set;}
}
public class Player
{
public int Id {get;set;}

public int PlayerSessionId {get;set;}
public PlayerSession Session {get;set;}
}

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

public int PlayerId {get;set;}
public Player Player {get;set;}
}
and efc takes care of the relationship for u under the hood
Qvabbyte
QvabbyteOP7d ago
okay
leowest
leowest7d ago
let me look where was the error moment
Qvabbyte
QvabbyteOP7d ago
on saveChangesAsync() before that I make new session and I want it to be added to db
leowest
leowest7d ago
in the image I can't really see the code it breaks or line let me look sec so line 30 on the first tab you could just do
player.ActiveSession = new PlayerSession
{
GameState = new GameState { CurrentBalance = player.Balance },
};
await _dbContext.SaveChangesAsync();
player.ActiveSession = new PlayerSession
{
GameState = new GameState { CurrentBalance = player.Balance },
};
await _dbContext.SaveChangesAsync();
Qvabbyte
QvabbyteOP7d ago
in that case I have to give playersession constructor wait oh no wait
leowest
leowest7d ago
if (player.ActiveSession == null || !player.ActiveSession.IsActive)
{
player.ActiveSession = new PlayerSession
{
GameState = new GameState { CurrentBalance = player.Balance },
};
await _dbContext.SaveChangesAsync();
}
if (player.ActiveSession == null || !player.ActiveSession.IsActive)
{
player.ActiveSession = new PlayerSession
{
GameState = new GameState { CurrentBalance = player.Balance },
};
await _dbContext.SaveChangesAsync();
}
all that code becomes just this it might be a problem is that player had a session already thou even if not active
Qvabbyte
QvabbyteOP7d ago
yeah that's the thing that player has the session
leowest
leowest7d ago
so u would need a separate condition to handle it if session is not nul
Qvabbyte
QvabbyteOP7d ago
Btw I tried to assign null to that, thats why I made playerSession nullable in first place
leowest
leowest7d ago
and change it appropriately and save
Qvabbyte
QvabbyteOP7d ago
but it gave me exactly same error as this
leowest
leowest7d ago
you can't just assign null to it you either delete the session attached to that player and create a new one or u update the one it already has
Qvabbyte
QvabbyteOP7d ago
yeah so update because I want sessions to be saved in table
leowest
leowest7d ago
if (player.ActiveSession is null)
{
player.ActiveSession = new PlayerSession
{
GameState = new GameState { CurrentBalance = player.Balance },
};
}
else
{
// handle session not being null
}
await _dbContext.SaveChangesAsync();
if (player.ActiveSession is null)
{
player.ActiveSession = new PlayerSession
{
GameState = new GameState { CurrentBalance = player.Balance },
};
}
else
{
// handle session not being null
}
await _dbContext.SaveChangesAsync();
but honestly to me it feels like a player could potentially have many sessions exception would be active vs terminated sessions? so one to many instead
Qvabbyte
QvabbyteOP7d ago
yeah so basically active session is the one that player is currently playing
leowest
leowest7d ago
so yeah that would be a one to many instead
Qvabbyte
QvabbyteOP7d ago
and other sessions which already has an EndDate which means it's not active anymore
leowest
leowest7d ago
as you have right now u would only have 1 entry for session per player
Qvabbyte
QvabbyteOP7d ago
HasOne to HasMany on model builder or just make ICollection
leowest
leowest7d ago
public class Player
{
public int Id {get;set;}

public List<PlayerSession> Sessions {get;set;}
}

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

public int PlayerId {get;set;}
public Player Player {get;set;}
}
public class Player
{
public int Id {get;set;}

public List<PlayerSession> Sessions {get;set;}
}

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

public int PlayerId {get;set;}
public Player Player {get;set;}
}
no need to do anything on model builder you can refer to https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#mapping-relationships-in-ef-core and what happens here is that u just pulled player from the db, and its being tracked so any changes you do will be reflected if u call savechanges so I dont need to create session and gamestate separately to save each
Qvabbyte
QvabbyteOP7d ago
wait does List work in ef? I was used to making ICollections
leowest
leowest7d ago
I can just create then the way I am doing and save changes will take care of the rest you can use either
Qvabbyte
QvabbyteOP7d ago
do you also mean to remove ActiveSession? and just leave it to Sessions and the one with no EndDate in code logic will be ActiveSession?
leowest
leowest7d ago
ideally u would use IReadOnlyCollection or ICollection
Qvabbyte
QvabbyteOP7d ago
and also should I just remove my modelBuilder code?
leowest
leowest7d ago
ICollection was often used for lazy loading which is not recommended unless u have a very specific scenario well u would query for the active session now
Qvabbyte
QvabbyteOP7d ago
var ActiveSession = player.Sessions.FirstOrDefault(s => s.IsActive);

// Initialize new session if needed
if (ActiveSession == null)
{
}else{

}
var ActiveSession = player.Sessions.FirstOrDefault(s => s.IsActive);

// Initialize new session if needed
if (ActiveSession == null)
{
}else{

}
leowest
leowest7d ago
ideally we dont use models directly we project the results with only the data we need to DTOs to avoid certain issues as well as separate concerns sure
Qvabbyte
QvabbyteOP7d ago
Yeah I know about DTos I didn't use it in this project it would make stuff easier tbh
leowest
leowest7d ago
ok
Qvabbyte
QvabbyteOP7d ago
it's just I straight away started working on logic and then try to optimize the code itself which will take a LONG time now I look at it
leowest
leowest7d ago
its not really optmization but ok 😉
Qvabbyte
QvabbyteOP7d ago
yeah I know it is not xd u should've seen before and in this case now we just add another session
leowest
leowest7d ago
sure
Qvabbyte
QvabbyteOP7d ago
okay I am switching up a lots of code now and I'll try if that works thanks for helping

Did you find this page helpful?