-
Notifications
You must be signed in to change notification settings - Fork 13
Description
For example, when INSERTing rows into both SESSION_MST (for UserSessionDto), and LOGIN_TRN_MST (for LoginDto), the following attribute decoration will result in an exception because Dapper.SimpleSave will try to insert the parent record before the child:
[Table("[user].SESSION_MST")]
public class UserSessionDto
{
[PrimaryKey]
public int? SessionKey { get; set; }
public DateTimeOffset StartDateTime { get; set; }
public DateTimeOffset? EndDateTime { get; set; }
[OneToOne]
[Column("LoginKey")]
public LoginDto Login { get; set; }
}For reference, LoginDto is defined as follows:
[Table("[user].LOGIN_TRN")]
public class LoginDto
{
[PrimaryKey]
public int? LoginKey { get; set; }
[OneToOne, Column("UserKey")]
[SimpleSaveIgnore]
[JsonIgnore]
[XmlIgnore]
public UserDto User { get; set; }
public int UserKey { get; set; }
[OneToOne, Column("AllowedIPKey")]
public AllowedIpDto AllowedIp { get; set; }
public int? AllowedIPKey { get; set; }
public UserLoginResultEnum LoginResultKey { get; set; }
public string UnknownIp { get; set; }
}At the moment Dapper.SimpleSave doesn't correctly handle the relationship by inserting the LoginDto row first. Instead, as already stated, it will try to insert the SessionDto row.
However the LoginDto row must be inserted first because, in this case, the foreign key column is defined in SESSION_MST and cannot contain NULLs:
CREATE TABLE [user].[SESSION_MST]
(
SessionKey INT NOT NULL IDENTITY(0,1)
CONSTRAINT [PK_SESSION_KEY] PRIMARY KEY,
[StartDateTime] DATETIMEOFFSET(4) NOT NULL,
[EndDateTime] DATETIMEOFFSET(4) NULL,
[LoginKey] INT NOT NULL INDEX [IX_SESSION_MST_LOGINKEY] NONCLUSTERED,
CONSTRAINT [FK_SESSION_MST_LOGIN_TRN] FOREIGN KEY ([LoginKey]) REFERENCES [user].[LOGIN_TRN]([LoginKey])
)In any case, inserting in LOGIN_TRN first would be cleaner because it results in only a single INSERT for the SESSION_MST, rather than an INSERT, followed by an UPDATE after the LOGIN_TRN INSERT.
This can be made to work properly now, but it requires a [ForeignKeyReference] attribute on the Login property. This forces Dapper.SimpleSave to correctly handle the relationship, and it will first INSERT into LOGIN_TRN, then into SESSION_MST. Here's the code:
[Table("[user].SESSION_MST")]
public class UserSessionDto
{
[PrimaryKey]
public int? SessionKey { get; set; }
public DateTimeOffset StartDateTime { get; set; }
public DateTimeOffset? EndDateTime { get; set; }
[OneToOne]
[ForeignKeyReference(typeof(LoginDto))] // Note the extra attribute
[Column("LoginKey")]
public LoginDto Login { get; set; }
}Dapper.SimpleSave should be able to infer any information it needs from the return type of the property without requiring an explicit (re-)statement using the [ForeignKeyReference] attribute.