I have this table that I want to track editing of other tables with.
namespace fais.accounting.Models.COA
{
public class coaEdits
{
public string entity_id { get; set; }
public string editor_id { get; set; }
[Display(Name = "Edited on")]
public DateTime occured_on { get; set; } = DateTime.Now;
public User editor { get; set; }
public string entityType { get; set; }
public string entity { get; set; } // If I coaEdits<Tkey> I can't set public Tkey entity, but it should be the object of the entityType's class right?
}
}
That table I hoped would work where the foreign keys would be the entity_id
.
All the other tables/classes are inheriting this class which has the navigation to the coaEdits
public class coa_base
{
[Column(Order = 2)]
public string title { get; set; }
[Display(Name = "Entry date")]
public DateTime entry_date { get; set; }
[Required]
[Display(Name = "Last updated")]
public DateTime updated_on { get; set; } = DateTime.Now;
public string creator_id { get; set; } //= @TODO userManage.get_current_user <- how to default?
public User creator { get; set; }
public virtual List<coaEdits> edits { get; set; }
}
Where an example of on of the tables is
public class budgets : coa_base
{
[Column(Order = 1)]
[Display(Name = "Budget")]
public string budget_id { get; set; }
public virtual List<coa> accounts { get; set; }
}
When I run
dotnet ef migrations add setsUpBaseClassModelOnCOA --context ApplicationDbContext
I get:
migrationBuilder.CreateTable(
name: "coaEdits",
columns: table => new
{
entity_id = table.Column<string>(nullable: false),
editor_id = table.Column<string>(nullable: false),
occured_on = table.Column<DateTime>(nullable: false),
editorId = table.Column<string>(nullable: true),
entity_type = table.Column<string>(maxLength: 200, nullable: false),
// everything below this seems like it should be wrapped up with
// key as entity_id and typed by entity_type, but it is not?
entity = table.Column<string>(nullable: true), // that should be an object? What the?
budgetsbudget_id = table.Column<string>(nullable: true), // should be it should be the entity_id
coaaccount_id = table.Column<string>(nullable: true), // didto
fundsfund_id = table.Column<string>(nullable: true), // didto
// and so on
},
constraints: table =>
{
table.PrimaryKey("PK_coaEdits", x => new { x.entity_id, x.editor_id, x.occured_on });
table.ForeignKey(
name: "FK_coaEdits_coa_budgets_budgetsbudget_id",
column: x => x.budgetsbudget_id,
principalTable: "coa_budgets",
principalColumn: "budget_id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_coaEdits_coa_accounts_coaaccount_id",
column: x => x.coaaccount_id,
principalTable: "coa_accounts",
principalColumn: "account_id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_coaEdits_AspNetUsers_editorId",
column: x => x.editorId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_coaEdits_coa_funds_fundsfund_id",
column: x => x.fundsfund_id,
principalTable: "coa_funds",
principalColumn: "fund_id",
onDelete: ReferentialAction.Restrict);
// and so on
});
while I was expecting to get:
migrationBuilder.CreateTable(
name: "coaEdits",
columns: table => new
{
entity_id = table.Column<string>(nullable: false),
editor_id = table.Column<string>(nullable: false),
occured_on = table.Column<DateTime>(nullable: false),
editorId = table.Column<string>(nullable: true),
entity_type = table.Column<string>(maxLength: 200, nullable: false),
},
constraints: table =>
{
table.PrimaryKey("PK_coaEdits", x => new { x.entity_id, x.editor_id, x.occured_on });
table.ForeignKey(
name: "FK_coaEdits_coa_budgets_budgetsbudget_id",
column: x => x.entity_id,
principalTable: "coa_budgets",
principalColumn: "budget_id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_coaEdits_coa_accounts_coaaccount_id",
column: x => x.entity_id,
principalTable: "coa_accounts",
principalColumn: "account_id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_coaEdits_AspNetUsers_editorId",
column: x => x.editorId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_coaEdits_coa_funds_fundsfund_id",
column: x => x.entity_id,
principalTable: "coa_funds",
principalColumn: "fund_id",
onDelete: ReferentialAction.Restrict);
// and so on
});
The fluent options on the builder are
builder.Entity<coaEdits>().HasKey(sc => new { sc.entity_id, sc.editor_id, sc.occured_on });
builder.Entity<coaEdits>(b =>
{
b.HasDiscriminator<string>("entityType");
b.Property(e => e.entityType)
.HasMaxLength(200)
.HasColumnName("entity_type");
});
builder.Entity<coa>().ToTable("coa_accounts");
builder.Entity<funds>().ToTable("coa_funds");
// and so on
What is the best way to correct this? Is there a better approach? The missing part to this is
public string entity { get; set; }
I can't seem to figure out how to turn that in to the class from the discriminator which I would expect to be able to do. Also wouldn't the discriminator need to be a key too so it would be a key of entity_type
and entity_id
EDIT/UPDATE: I was trying to get that type to get applied like this
public class coaEdits<TKey>
{
public string entity_id { get; set; }
public string editor_id { get; set; }
[Display(Name = "Edited on")]
public DateTime occured_on { get; set; } = DateTime.Now;
public User editor { get; set; }
public string entityType { get; set; }
[NotMapped]
public TKey entity { get; set; }
}
And the base class like this:
public class coa_base<TKey>
{
[Column(Order = 2)]
public string title { get; set; }
[Display(Name = "Entry date")]
public DateTime entry_date { get; set; }
[Required]
[Display(Name = "Last updated")]
public DateTime updated_on { get; set; } = DateTime.Now;
public string creator_id { get; set; } //= userManage.get_current_user
public User creator { get; set; }
public virtual List<coaEdits<TKey>> edits { get; set; }
}
Where an example class would be like
public class budgets : coa_base<budgets>
{
[Column(Order = 1)]
[Display(Name = "Budget")]
public string budget_id { get; set; }
public virtual List<coa> accounts { get; set; }
}
Using this instead for the fluent on builder options
builder.Entity<coaEdits<IKey>>().HasKey(sc => new { sc.entity_id, sc.editor_id, sc.occured_on });
builder.Entity<coaEdits<IKey>>(b =>
{
b.HasDiscriminator<string>("entityType");
b.Property(e => e.entityType).HasColumnName("entity_type");
});
But I get this error instead now
The entity type 'coaEdits' requires a primary key to be defined.
but that is set I thought by
builder.Entity<coaEdits<IKey>>().HasKey(sc => new { sc.entity_id, sc.editor_id, sc.occured_on });
If I test it with it all hard coded in, where I do
builder.Entity<coaEdits<budgets>>() . .
I get a table of coaEdits<budgets>
wanting to be created.
If I try to counter that table name with builder.Entity<coaEdits<budgets>>().ToTable("coa_edits");
it then says that another one, coaEdits<funds>
and coaEdits<budgets>
can't share the table because they don't share a primary key, but they do I thought entity_id
Additional UPDATE: Continuing on the coaEdits<TKey>
approach so that public string entity { get; set; }
gets a type, I figured that if the fluent api was not working to set the primary key, that I would try to use the attributes to get around this, but that has produced this error
Entity type
'coaEdits<budgets>'
has composite primary key defined with data annotations. To set composite primary key, use fluent API.
From just doing this.
public class coaEdits<TKey>
{
[Key]
public string entity_id { get; set; }
[Key]
public string editor_id { get; set; }
// and so on
ADDITIONAL INFORMATION: if I alter the migration the way I am thinking it should be set up to get the table correct, I get another error that doesn't make sense in the context. Here i will set the coaEdits table first
migrationBuilder.CreateTable(name: "coaEdits", columns: table => new {
entity_id = table.Column<string>(nullable: true)
, editor_id = table.Column<string>(nullable: true)
, occured_on = table.Column<DateTime>(nullable: false)
, entity_type = table.Column<string>(nullable: false)
}, constraints: table => {
table.PrimaryKey("PK_coaEdits", x => x.occured_on);
table.ForeignKey(name: "FK_coaEdits_AspNetUsers_editorId", column: x => x.editor_id
, principalTable: "AspNetUsers", principalColumn: "Id"
, onDelete: ReferentialAction.Restrict);
table.ForeignKey(name: "FK_coaEdits_coa_budgets_entity_id", column: x => x.entity_id
, principalTable: "coa_budgets", principalColumn: "budget_id"
, onDelete: ReferentialAction.SetNull);
table.ForeignKey(name: "FK_coaEdits_coa_funds_entity_id", column: x => x.entity_id
, principalTable: "coa_funds", principalColumn: "fund_id"
, onDelete: ReferentialAction.SetNull);
// and so on
});
And the example entity budgets is set up like this
migrationBuilder.CreateTable(
name: "coa_budgets",
columns: table => new
{
entry_date = table.Column<DateTime>(nullable: false),
updated_on = table.Column<DateTime>(nullable: false),
title = table.Column<string>(nullable: true),
creator_id = table.Column<string>(nullable: true),
budget_id = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_coa_budgets", x => x.budget_id);
table.ForeignKey(
name: "FK_coa_budgets_AspNetUsers_creator_id",
column: x => x.creator_id,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
but when I run dotnet ef database update --context ApplicationDbContext
leading to this error
Column 'coa_budgets.budget_id' is not the same length or scale as referencing column 'coaEdits.entity_id' in foreign key 'FK_coaEdits_coa_budgets_entity_id'. Columns participating in a foreign key relationship must be defined with the same length and scale.
The full error is:
Failed executing DbCommand (21ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE [coaEdits] (
[entity_id] nvarchar(max) NULL,
[editor_id] nvarchar(max) NULL,
[occured_on] datetime2 NOT NULL,
[entity_type] nvarchar(max) NOT NULL,
CONSTRAINT [PK_coaEdits] PRIMARY KEY ([occured_on]),
CONSTRAINT [FK_coaEdits_AspNetUsers_editor_id] FOREIGN KEY ([editor_id]) REFERENCES [AspNetUsers] ([Id]) ON DELETE NO ACTION,
CONSTRAINT [FK_coaEdits_coa_budgets_entity_id] FOREIGN KEY ([entity_id]) REFERENCES [coa_budgets] ([budget_id]) ON DELETE SET NULL,
CONSTRAINT [FK_coaEdits_coa_funds_entity_id] FOREIGN KEY ([entity_id]) REFERENCES [coa_funds] ([fund_id]) ON DELETE SET NULL,
CONSTRAINT [FK_coaEdits_coa_objects_entity_id] FOREIGN KEY ([entity_id]) REFERENCES [coa_objects] ([object_id]) ON DELETE SET NULL,
CONSTRAINT [FK_coaEdits_coa_programs_entity_id] FOREIGN KEY ([entity_id]) REFERENCES [coa_programs] ([program_id]) ON DELETE SET NULL,
CONSTRAINT [FK_coaEdits_coa_projects_entity_id] FOREIGN KEY ([entity_id]) REFERENCES [coa_projects] ([project_id]) ON DELETE SET NULL,
CONSTRAINT [FK_coaEdits_coa_accounts_entity_id] FOREIGN KEY ([entity_id]) REFERENCES [coa_accounts] ([account_id]) ON DELETE SET NULL,
CONSTRAINT [FK_coaEdits_coa_subobjects_entity_id] FOREIGN KEY ([entity_id]) REFERENCES [coa_subobjects] ([subobject_id]) ON DELETE SET NULL,
CONSTRAINT [FK_coaEdits_coa_subfunds_entity_id] FOREIGN KEY ([entity_id]) REFERENCES [coa_subfunds] ([subfund_id]) ON DELETE SET NULL
);
System.Data.SqlClient.SqlException (0x80131904): Column 'coa_budgets.budget_id' is not the same length or scale as referencing column 'coaEdits.entity_id' in foreign key 'FK_coaEdits_coa_budgets_entity_id'. Columns participating in a foreign key relationship must be defined with the same length and scale.
As I read everything, I have set up all the foreign keys matching.. the entity_id is [entity_id] nvarchar(max) NULL,
where the budget is [budget _id] nvarchar(max) NULL,
so I'm not sure why SQL threw that error.
User contributions licensed under CC BY-SA 3.0