c# - net - entity framework visual studio 2017
Entity Framework 6 GUID como clave principal: No se puede insertar el valor NULL en la columna ''Id'', tabla ''FileStore''; la columna no permite nulos (10)
¿Y qué tal algo así?
public class Carrier : Entity
{
public Carrier()
{
this.Id = Guid.NewGuid();
}
public Guid Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
Tengo una entidad con la clave principal "Id" que es Guid:
public class FileStore
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Path { get; set; }
}
Y alguna configuración:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<FileStore>().Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
base.OnModelCreating(modelBuilder);
}
Cuando intento insertar un registro, aparece un siguiente error:
No se puede insertar el valor NULL en la columna ''Id'', tabla ''FileStore''; la columna no permite nulos. INSERT falla. / R / nLa declaración ha finalizado.
No quiero generar Guid manualmente. Solo quiero insertar un registro y obtener Id
generado por SQL Server. Si configuro .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
, la columna Id
no es la columna Identity en SQL Server.
¿Cómo puedo configurar Entity Framework para autogenerar Guid en SQL Server?
Además de agregar estos atributos a su columna Id:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
en su migración debe cambiar su CreateTable
para agregar la propiedad defaultValueSQL
a su columna, es decir:
Id = c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"),
Esto evitará que tenga que tocar manualmente su base de datos que, como señaló en los comentarios, es algo que desea evitar con Code First.
De acuerdo con this , DatabaseGeneratedOption.Identity no es detectado por una migración específica si se agrega después de que se haya creado la tabla, que es el caso en el que me encuentro. Así que dejé la base de datos y esa migración específica y agregué una nueva migración, finalmente actualicé la base de datos, luego todo funcionó como se esperaba. Estoy usando EF 6.1, SQL2014 y VS2013.
Esto funciona para mí (no Azure), SQL 2008 R2 en el servidor de desarrollo o localdb / mssqllocaldb en la estación de trabajo local. Nota: la entidad agrega columnas Create, CreateBy, Modified, ModifiedBy y Version.
public class Carrier : Entity
{
public Guid Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
luego crea una clase de configuración de mapeo
public class CarrierMap : EntityTypeConfiguration<Carrier>
{
public CarrierMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(p => p.Code)
.HasMaxLength(4)
.IsRequired()
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute { IsClustered = true, IsUnique = true }));
Property(p => p.Name).HasMaxLength(255).IsRequired();
Property(p => p.Created).HasPrecision(7).IsRequired();
Property(p => p.Modified)
.HasColumnAnnotation("IX_Modified", new IndexAnnotation(new IndexAttribute()))
.HasPrecision(7)
.IsRequired();
Property(p => p.CreatedBy).HasMaxLength(50).IsRequired();
Property(p => p.ModifiedBy).HasMaxLength(50).IsRequired();
Property(p => p.Version).IsRowVersion();
}
}
Esto crea un método Up en la DbMigration inicial cuando ejecuta add-migration como este
CreateTable(
"scoFreightRate.Carrier",
c => new
{
Id = c.Guid(nullable: false, identity: true),
Code = c.String(nullable: false, maxLength: 4),
Name = c.String(nullable: false, maxLength: 255),
Created = c.DateTimeOffset(nullable: false, precision: 7),
CreatedBy = c.String(nullable: false, maxLength: 50),
Modified = c.DateTimeOffset(nullable: false, precision: 7,
annotations: new Dictionary<string, AnnotationValues>
{
{
"IX_Modified",
new AnnotationValues(oldValue: null, newValue: "IndexAnnotation: { }")
},
}),
ModifiedBy = c.String(nullable: false, maxLength: 50),
Version = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
})
.PrimaryKey(t => t.Id)
.Index(t => t.Code, unique: true, clustered: true);
Nota: que las columnas Id no obtienen un valor predeterminado, no se preocupe
Ahora ejecuta Update-Database, y deberías terminar con una definición de tabla en tu base de datos como esta:
CREATE TABLE [scoFreightRate].[Carrier] (
[Id] UNIQUEIDENTIFIER DEFAULT (newsequentialid()) NOT NULL,
[Code] NVARCHAR (4) NOT NULL,
[Name] NVARCHAR (255) NOT NULL,
[Created] DATETIMEOFFSET (7) NOT NULL,
[CreatedBy] NVARCHAR (50) NOT NULL,
[Modified] DATETIMEOFFSET (7) NOT NULL,
[ModifiedBy] NVARCHAR (50) NOT NULL,
[Version] ROWVERSION NOT NULL,
CONSTRAINT [PK_scoFreightRate.Carrier] PRIMARY KEY NONCLUSTERED ([Id] ASC)
);
GO
CREATE UNIQUE CLUSTERED INDEX [IX_Code]
ON [scoFreightRate].[Carrier]([Code] ASC);
Nota: hemos reemplazado al SqlServerMigrationSqlGenerator para garantizar que NO haga que la clave principal sea un índice agrupado, ya que alentamos a nuestros desarrolladores a establecer un mejor índice agrupado en las tablas.
public class OurMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate(AddPrimaryKeyOperation addPrimaryKeyOperation)
{
if (addPrimaryKeyOperation == null) throw new ArgumentNullException("addPrimaryKeyOperation");
if (!addPrimaryKeyOperation.Table.Contains("__MigrationHistory"))
addPrimaryKeyOperation.IsClustered = false;
base.Generate(addPrimaryKeyOperation);
}
protected override void Generate(CreateTableOperation createTableOperation)
{
if (createTableOperation == null) throw new ArgumentNullException("createTableOperation");
if (!createTableOperation.Name.Contains("__MigrationHistory"))
createTableOperation.PrimaryKey.IsClustered = false;
base.Generate(createTableOperation);
}
protected override void Generate(MoveTableOperation moveTableOperation)
{
if (moveTableOperation == null) throw new ArgumentNullException("moveTableOperation");
if (!moveTableOperation.CreateTableOperation.Name.Contains("__MigrationHistory")) moveTableOperation.CreateTableOperation.PrimaryKey.IsClustered = false;
base.Generate(moveTableOperation);
}
}
Me pasó antes.
Cuando se creó la tabla y agregué .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
más adelante, la migración del código de alguna manera no pudo asignar el valor predeterminado para la columna Guid.
La solución:
Todo lo que necesitamos es ir a la base de datos, seleccionar la columna Id y agregar newsequentialid()
manualmente en Default Value or Binding
.
No es necesario actualizar dbo .__ MigrationHistory table.
Espero eso ayude.
La solución de agregar New Guid()
generalmente no se prefiere, porque en teoría existe la posibilidad de que pueda obtener un duplicado accidentalmente.
Y no debe preocuparse por la edición directa en la base de datos. Todo el Framework de Entity hace es automatizar parte de nuestro trabajo de base de datos.
Traductorio
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
dentro
[Id] [uniqueidentifier] NOT NULL DEFAULT newsequentialid(),
Si, de alguna manera, nuestro EF perdiera una cosa y no agregara el valor predeterminado para nosotros, simplemente continúe y agréguelo manualmente.
No puedes. Romperás / romperás muchas cosas. Como las relaciones. Lo cual depende de que se retire el número que EF no puede hacer en la forma en que lo configura. El precio por romper cada patrón que hay.
Genere el GUID en la capa C #, para que las relaciones puedan seguir funcionando.
Puede establecer el valor predeterminado de su Id en su base de datos en newsequentialid () o newid (). Entonces la configuración de identidad de EF debería funcionar.
Si usa Code-First y ya tiene una base de datos:
public override void Up()
{
AlterColumn("dbo.MyTable","Id", c => c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"));
}
prueba esto :
public class FileStore
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
public string Path { get; set; }
}
Puedes consultar esta publicación SO .
Marco de la entidad: utilice un Guid como clave principal
Usar un Guid como la clave principal de su tabla, cuando usa Entity Framework, requiere un poco más de esfuerzo que cuando usa un entero. El proceso de instalación es sencillo, después de haber leído / mostrado cómo hacerlo.
El proceso es ligeramente diferente para los enfoques Code First y Database First. Esta publicación analiza ambas técnicas.
Primero el código
Usar un Guid como la clave principal al tomar el primer acercamiento del código es simple. Al crear su entidad, agregue el atributo DatabaseGenerated a su propiedad de clave principal, como se muestra a continuación;
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
El marco de entidad creará la columna como era de esperar, con una clave primaria y un tipo de datos uniqueidentifier.
codefirst-defaultvalue
También note, muy importante, que el valor predeterminado en la columna se ha establecido en (newsequentialid())
. Esto genera un nuevo Guid secuencial (continuo) para cada fila. Si estuvieras tan inclinado, podrías cambiar esto a newid()
), lo que daría como resultado un Guid completamente aleatorio para cada nueva fila. Esto se borrará cada vez que su base de datos se descarte y vuelva a crear, por lo que esto funciona mejor cuando se utiliza el enfoque de Base de datos.
Base de datos primero
El primer enfoque de la base de datos sigue una línea similar al primer enfoque del código, pero tendrá que editar manualmente su modelo para que funcione.
Asegúrese de editar la columna de la clave principal y agregar la función (newsequentialid ()) o (newid ()) como valor predeterminado antes de hacer cualquier cosa.
A continuación, abra su diagrama EDMX, seleccione la propiedad adecuada y abra la ventana de propiedades. Asegúrese de que StoreGeneratedPattern esté configurado en identidad.
databasefirst-model
No es necesario dar a su entidad una identificación en su código, que se completará para usted automáticamente después de que la entidad se haya comprometido con la base de datos;
using (ApplicationDbContext context = new ApplicationDbContext())
{
var person = new Person
{
FirstName = "Random",
LastName = "Person";
};
context.People.Add(person);
context.SaveChanges();
Console.WriteLine(person.Id);
}
Nota importante: su campo Guid DEBE ser una clave principal, o esto no funciona. ¡Entity Framework te dará un mensaje de error bastante críptico!
Resumen
Guid (identificadores únicos globales) se puede usar fácilmente como claves principales en Entity Framework. Se requiere un pequeño esfuerzo adicional para hacer esto, dependiendo del enfoque que esté tomando. Cuando use el primer acercamiento de código, agregue el atributo DatabaseGenerated a su campo clave. Al utilizar Database First, establezca explícitamente StoredGeneratedPattern en Identity en su modelo.
[1]: https://i.stack.imgur.com/IxGdd.png
[2]: https://i.stack.imgur.com/Qssea.png