¿Hay alguna forma "elegante" de dar a la propiedad específica un valor predeterminado?
Tal vez por DataAnnotations, algo así como:
[DefaultValue("true")]
public bool Active { get; set; }
Gracias.
public bool Inactive { get; set; }
😉
¿Hay alguna forma "elegante" de dar a la propiedad específica un valor predeterminado?
Tal vez por DataAnnotations, algo así como:
[DefaultValue("true")]
public bool Active { get; set; }
Gracias.
public bool Inactive { get; set; }
😉
Respuestas:
Puede hacerlo editando manualmente la primera migración de código:
public override void Up()
{
AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
}
Active
a true
la hora de crear un Event
objeto así. El valor predeterminado siempre estará false
en una propiedad bool no anulable, por lo que, a menos que se cambie, esto es lo que el marco de la entidad guardará en la base de datos. ¿O me estoy perdiendo algo?
Ha pasado un tiempo, pero dejando una nota para los demás. Logré lo que se necesita con un atributo y decoré mis campos de clase de modelo con ese atributo como quiero.
[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }
Obtuve la ayuda de estos 2 artículos:
Lo que hice:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlDefaultValueAttribute : Attribute
{
public string DefaultValue { get; set; }
}
modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));
private void SetAnnotatedColumn(ColumnModel col)
{
AnnotationValues values;
if (col.Annotations.TryGetValue("SqlDefaultValue", out values))
{
col.DefaultValueSql = (string)values.NewValue;
}
}
Luego, en el constructor Configuración de migración, registre el generador SQL personalizado.
SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());
[SqlDefaultValue(DefaultValue = "getutcdate()")]
en cada entidad. 1) Simplemente elimine modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));
2) AgregarmodelBuilder.Properties().Where(x => x.PropertyType == typeof(DateTime)).Configure(c => c.HasColumnType("datetime2").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed).HasColumnAnnotation("SqlDefaultValue", "getdate()"));
Las respuestas anteriores realmente ayudaron, pero solo entregaron parte de la solución. El problema principal es que tan pronto como elimine el atributo Valor predeterminado, la restricción en la columna de la base de datos no se eliminará. Por lo tanto, el valor predeterminado anterior permanecerá en la base de datos.
Aquí hay una solución completa al problema, incluida la eliminación de restricciones de SQL en la eliminación de atributos. También estoy reutilizando el .NET Framework nativoDefaultValue
atributo .
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }
Para que esto funcione, necesita actualizar IdentityModels.cs y Configuration.cs archivos
Agregue / actualice este método en su ApplicationDbContext
clase
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var convention = new AttributeToColumnAnnotationConvention<DefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.SingleOrDefault().Value.ToString());
modelBuilder.Conventions.Add(convention);
}
Actualice su Configuration
constructor de clases registrando un generador SQL personalizado, como este:
internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
public Configuration()
{
// DefaultValue Sql Generator
SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
}
}
A continuación, agregue la clase de generador de SQL personalizado (puede agregarlo al archivo Configuration.cs o a un archivo separado)
internal class DefaultValueSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
private int dropConstraintCount = 0;
protected override void Generate(AddColumnOperation addColumnOperation)
{
SetAnnotatedColumn(addColumnOperation.Column, addColumnOperation.Table);
base.Generate(addColumnOperation);
}
protected override void Generate(AlterColumnOperation alterColumnOperation)
{
SetAnnotatedColumn(alterColumnOperation.Column, alterColumnOperation.Table);
base.Generate(alterColumnOperation);
}
protected override void Generate(CreateTableOperation createTableOperation)
{
SetAnnotatedColumns(createTableOperation.Columns, createTableOperation.Name);
base.Generate(createTableOperation);
}
protected override void Generate(AlterTableOperation alterTableOperation)
{
SetAnnotatedColumns(alterTableOperation.Columns, alterTableOperation.Name);
base.Generate(alterTableOperation);
}
private void SetAnnotatedColumn(ColumnModel column, string tableName)
{
AnnotationValues values;
if (column.Annotations.TryGetValue("SqlDefaultValue", out values))
{
if (values.NewValue == null)
{
column.DefaultValueSql = null;
using (var writer = Writer())
{
// Drop Constraint
writer.WriteLine(GetSqlDropConstraintQuery(tableName, column.Name));
Statement(writer);
}
}
else
{
column.DefaultValueSql = (string)values.NewValue;
}
}
}
private void SetAnnotatedColumns(IEnumerable<ColumnModel> columns, string tableName)
{
foreach (var column in columns)
{
SetAnnotatedColumn(column, tableName);
}
}
private string GetSqlDropConstraintQuery(string tableName, string columnName)
{
var tableNameSplittedByDot = tableName.Split('.');
var tableSchema = tableNameSplittedByDot[0];
var tablePureName = tableNameSplittedByDot[1];
var str = $@"DECLARE @var{dropConstraintCount} nvarchar(128)
SELECT @var{dropConstraintCount} = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'{tableSchema}.[{tablePureName}]')
AND col_name(parent_object_id, parent_column_id) = '{columnName}';
IF @var{dropConstraintCount} IS NOT NULL
EXECUTE('ALTER TABLE {tableSchema}.[{tablePureName}] DROP CONSTRAINT [' + @var{dropConstraintCount} + ']')";
dropConstraintCount = dropConstraintCount + 1;
return str;
}
}
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
atributo? Si es así, ¿por qué? En mis pruebas parecía no tener ningún efecto dejándolo fuera.
Las propiedades de su modelo no tienen que ser 'propiedades automáticas' aunque eso sea más fácil. Y el atributo DefaultValue es realmente solo metadatos informativos. La respuesta aceptada aquí es una alternativa al enfoque del constructor.
public class Track
{
private const int DEFAULT_LENGTH = 400;
private int _length = DEFAULT_LENGTH;
[DefaultValue(DEFAULT_LENGTH)]
public int LengthInMeters {
get { return _length; }
set { _length = value; }
}
}
vs.
public class Track
{
public Track()
{
LengthInMeters = 400;
}
public int LengthInMeters { get; set; }
}
Esto solo funcionará para aplicaciones que crean y consumen datos utilizando esta clase específica. Por lo general, esto no es un problema si el código de acceso a datos está centralizado. Para actualizar el valor en todas las aplicaciones, debe configurar el origen de datos para establecer un valor predeterminado. La respuesta de Devi muestra cómo se puede hacer usando migraciones, sql o cualquier idioma que hable su fuente de datos.
Lo que hice, inicialicé valores en el constructor de la entidad
Nota: los atributos DefaultValue no establecerán los valores de sus propiedades automáticamente, debe hacerlo usted mismo
Después del comentario de @SedatKapanoglu, estoy agregando todo mi enfoque que funciona, porque tenía razón, solo usar la API fluida no funciona.
1- Crear generador de código personalizado y anular Generar para un modelo de columna.
public class ExtendedMigrationCodeGenerator : CSharpMigrationCodeGenerator
{
protected override void Generate(ColumnModel column, IndentedTextWriter writer, bool emitName = false)
{
if (column.Annotations.Keys.Contains("Default"))
{
var value = Convert.ChangeType(column.Annotations["Default"].NewValue, column.ClrDefaultValue.GetType());
column.DefaultValue = value;
}
base.Generate(column, writer, emitName);
}
}
2- Asigna el nuevo generador de código:
public sealed class Configuration : DbMigrationsConfiguration<Data.Context.EfSqlDbContext>
{
public Configuration()
{
CodeGenerator = new ExtendedMigrationCodeGenerator();
AutomaticMigrationsEnabled = false;
}
}
3- Usa una API fluida para crear la Anotación:
public static void Configure(DbModelBuilder builder){
builder.Entity<Company>().Property(c => c.Status).HasColumnAnnotation("Default", 0);
}
¡Es sencillo! Solo anote con requerido.
[Required]
public bool MyField { get; set; }
La migración resultante será:
migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);
Si desea verdadero, cambie el valor predeterminado a verdadero en la migración antes de actualizar la base de datos
true
?
Admito que mi enfoque escapa al concepto completo de "Código Primero". Pero si tiene la capacidad de cambiar el valor predeterminado en la tabla en sí ... es mucho más simple que las longitudes que tiene que pasar arriba ... ¡Soy demasiado vago para hacer todo ese trabajo!
Casi parece que la idea original de los carteles funcionaría:
[DefaultValue(true)]
public bool IsAdmin { get; set; }
Pensé que simplemente cometieron el error de agregar citas ... pero desgraciadamente no hay tanta intuición. Las otras sugerencias fueron demasiado para mí (dado que tengo los privilegios necesarios para entrar en la mesa y hacer los cambios ... donde no todos los desarrolladores lo harán en cada situación). Al final lo hice a la antigua usanza. Configuré el valor predeterminado en la tabla de SQL Server ... Quiero decir, ¡ya es suficiente! NOTA: Realicé una prueba adicional haciendo una migración de bases de datos y actualizaciones y los cambios se atascaron.
Simplemente sobrecargue el constructor predeterminado de la clase Modelo y pase cualquier parámetro relevante que pueda o no usar. Con esto, puede proporcionar fácilmente valores predeterminados para los atributos. A continuación se muestra un ejemplo.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Aim.Data.Domain
{
[MetadataType(typeof(LoginModel))]
public partial class Login
{
public Login(bool status)
{
this.CreatedDate = DateTime.Now;
this.ModifiedDate = DateTime.Now;
this.Culture = "EN-US";
this.IsDefaultPassword = status;
this.IsActive = status;
this.LoginLogs = new HashSet<LoginLog>();
this.LoginLogHistories = new HashSet<LoginLogHistory>();
}
}
public class LoginModel
{
[Key]
[ScaffoldColumn(false)]
public int Id { get; set; }
[Required]
public string LoginCode { get; set; }
[Required]
public string Password { get; set; }
public string LastPassword { get; set; }
public int UserGroupId { get; set; }
public int FalseAttempt { get; set; }
public bool IsLocked { get; set; }
public int CreatedBy { get; set; }
public System.DateTime CreatedDate { get; set; }
public Nullable<int> ModifiedBy { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public string Culture { get; set; }
public virtual ICollection<LoginLog> LoginLogs { get; set; }
public virtual ICollection<LoginLogHistory> LoginLogHistories { get; set; }
}
}
Consideremos que tiene un nombre de clase llamado Productos y que tiene un campo IsActive. solo necesitas un constructor de creación:
Public class Products
{
public Products()
{
IsActive = true;
}
public string Field1 { get; set; }
public string Field2 { get; set; }
public bool IsActive { get; set; }
}
¡Entonces su valor predeterminado de IsActive es True!
Edite :
si quieres hacer esto con SQL usa este comando:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.IsActive)
.HasDefaultValueSql("true");
}
En EF core, lanzado el 27 de junio de 2016, puede usar una API fluida para establecer el valor predeterminado. Vaya a la clase ApplicationDbContext, busque / cree el nombre del método OnModelCreating y agregue la siguiente API fluida.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<YourTableName>()
.Property(b => b.Active)
.HasDefaultValue(true);
}
En .NET Core 3.1 puede hacer lo siguiente en la clase de modelo:
public bool? Active { get; set; }
En DbContext OnModelCreating agrega el valor predeterminado.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Foundation>()
.Property(b => b.Active)
.HasDefaultValueSql("1");
base.OnModelCreating(modelBuilder);
}
Resultando en lo siguiente en la base de datos
Nota: Si no tiene nullable (bool?) Para su propiedad, recibirá la siguiente advertencia
The 'bool' property 'Active' on entity type 'Foundation' is configured with a database-generated default. This default will always be used for inserts when the property has the value 'false', since this is the CLR default for the 'bool' type. Consider using the nullable 'bool?' type instead so that the default will only be used for inserts when the property value is 'null'.
Descubrí que solo usando Auto-Property Initializer en la propiedad de la entidad es suficiente para hacer el trabajo.
Por ejemplo:
public class Thing {
public bool IsBigThing{ get; set; } = false;
}
Hmm ... hago DB primero, y en ese caso, esto es realmente mucho más fácil. EF6 ¿verdad? Simplemente abra su modelo, haga clic derecho en la columna para la que desea establecer un valor predeterminado, elija propiedades y verá un campo "Valor predeterminado". Solo complete eso y ahorre. Configurará el código para usted.
Sin embargo, su kilometraje puede variar en el código primero, no he trabajado con eso.
El problema con muchas otras soluciones es que, si bien pueden funcionar inicialmente, tan pronto como reconstruya el modelo, arrojará cualquier código personalizado que haya insertado en el archivo generado por la máquina.
Este método funciona agregando una propiedad adicional al archivo edmx:
<EntityType Name="Thingy">
<Property Name="Iteration" Type="Int32" Nullable="false" **DefaultValue="1"** />
Y agregando el código necesario al constructor:
public Thingy()
{
this.Iteration = 1;
Establezca el valor predeterminado para la columna en la tabla en el servidor MSSQL y en el atributo de agregar código de clase, de esta manera:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
por la misma propiedad
this.Active = true;
? Creo que el valor de la base de datos tendrá prioridad cuando se recupere, pero tenga cuidado si se realiza una nueva incorporación y luego se adjunta una entidad para una actualización sin una recuperación primero, ya que el seguimiento de cambios podría ver esto como que desea actualizar el valor. Comente porque no he usado EF en mucho tiempo, y siento que esta es una toma en la oscuridad.