问题
How do I get this column as similar to a PERSISTED COMPUTED column in the database?
My current attempt (it loads all CompCol rows with null in seed) :
public class Call
{
public Call()
{
}
[Key]
public int Id { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string CompCol
{
get
{
return "ABC-" + Convert.ToString(Id).PadLeft(5, '0');
}
protected set {}
}
}
回答1:
The solution I found was to :
Make sure auto migrations are turned off. This is so that VS will generate a script (fluent api code) for us to further customise instead of just running it. So in the configuration class :
public Configuration() { AutomaticMigrationsEnabled = false; }
Add the field to the class and set it as computed like so, the setter is private because we obviously cannot write to a computed field :
[DatabaseGenerated(DatabaseGeneratedOption.Computed)] public string BreakdownNo { get; private set; }
Then do an
add-migration [xyz-name]
in the Package Manager Console to generate the migration code, which will appear under the migrations folder with the given name.Inside the migration comment out the code in
Up()
and add custom SQL like so :public override void Up() { //AddColumn("dbo.Calls", "BreakdownNo", c => c.String()); Sql("ALTER TABLE dbo.Calls ADD BreakdownNo AS ('BD'+RIGHT('00000'+ CAST(Id AS VARCHAR), 6))"); }
Do an
update-database
in the PM and it should add the computed column properly.
FURTHER NOTES : If you get the formula wrong then you will have to revert back the migration by doing an update-database -targetMigration: [name of migration to go back to]
then do another add-migration name
and amend your formula there, finishing off with update-database. There may be a better way but this is what I found and used.
I did not however find a way to make the field persisted yet.
回答2:
Why not calling sql like that:
public class demo
{
void demoMethod()
{
Model1 model = new Model1();//Model1 : DbContext
model.Database.ExecuteSqlCommand("alter table Results drop column Total; alter table Results add Total AS (Arabic + English + Math + Science)");
}
}
回答3:
I ran into some trouble using the method proposed in the accepted answer. I offer an alternative solution that worked for me.
I encountered a failure when running this query:
oDb.LogEntries.SingleOrDefault(Function(LogEntry) LogEntry.LogTime = dDate)
The error message:
The 'MinutesOffline' property on 'LogEntry' could not be set to a 'System.Int32' value. You must set this property to a non-null value of type 'System.Single'.
As we can see, EF 6.2 is trying to write a value to the property. Whether this is due to EF internally attempting to write to the Private Set
, I don't know. It almost seems like it. But the end result is what matters: the statement fails.
Instead of setting the column to DatabaseGeneratedOption.Computed
, I ended up ignoring it altogether: Builder.Entity(Of LogEntry).Ignore(Function(LogEntry) LogEntry.MinutesOffline)
.
This enabled me to create a read-only property:
Public ReadOnly Property MinutesOffline As Single
Get
Return IIf(Me.Scale < 1, 5, 0)
End Get
End Property
It also has the added benefit that we don't have to comment out any lines in the generated migration.
We still have to make the custom Sql()
call in Up()
:
ALTER TABLE [LogEntries] ADD [MinutesOffline] AS (IIF([Scale] < 1, 5, 0)) PERSISTED
...and the PERSISTED
keyword does work here. This becomes a persisted computed column.
YMMV
--EDIT--
I found out why I was getting the casting error; it had nothing to do with migrations and everything to do with my code. I wasn't properly casting the computed column at creation time:
ALTER TABLE [LogEntries] ADD [MinutesOffline] AS (IIF([Scale] < 1, 5, 0)) PERSISTED
The proper syntax for doing so is this:
ALTER TABLE [LogEntries] ADD [MinutesOffline] AS (CAST((IIF([Scale] < 1, 5, 0)) AS REAL)) PERSISTED
Accordingly, I've reverted the Ignore()
calls and switched everything back over to the method proposed in the accepted answer.
Hat tip to JotaBe for the assistance.
来源:https://stackoverflow.com/questions/14283519/how-to-make-persisted-computed-column-in-ef-code-first