update the migrations framework first existing example create context code c# entity-framework entity-framework-4 poco database-first

c# - the - entity framework database first



¿Es posible evitar que EntityFramework 4 sobrescriba las propiedades personalizadas? (6)

Estoy utilizando EF 4 Database primero + POCOs. Debido a que EF no tiene una manera fácil de indicar que los DateTimes entrantes son del tipo UTC, moví la propiedad del archivo generado automáticamente a una clase parcial en otro archivo.

private DateTime _createdOn; public virtual System.DateTime CreatedOn { get { return _createdOn; } set { _createdOn = (value.Kind == DateTimeKind.Unspecified) ? _createdOn = DateTime.SpecifyKind(value, DateTimeKind.Utc) : value; } }

Sin embargo, ahora cada vez que actualizo el modelo, las propiedades automáticas se vuelven a crear en la generación T4. Por supuesto, esto causa el siguiente error de compilación: "El tipo ''Foo'' ya contiene una definición para ''CreatedOn''".

¿Hay alguna manera de decirle a EF que no genere esa propiedad y que me permita manejarlo solo?

Actualizar

Gracias por las respuestas de todos ...

Creé una nueva propiedad personalizada con un nombre diferente.

public virtual System.DateTime CreatedOnUtc { get { return (CreatedOn.Kind==DateTimeKind.Unspecified) ? DateTime.SpecifyKind(CreatedOn, DateTimeKind.Utc) : CreatedOn; } set { CreatedOn = (value.Kind == DateTimeKind.Unspecified) ? CreatedOn = DateTime.SpecifyKind(value, DateTimeKind.Utc) : value; } }

También configuré todos los setters y getters de la propiedad autogenerada a Private a excepción de aquellas propiedades que necesitaba usar en una consulta de Linq-to-Entities (suspiro). En esos casos, establecí esos getters en internos.

Me gustaría que hubiera un menú desplegable en los tipos DateTime para especificar qué "Tipo" de DateTime debería tratar EF. Eso hubiera ahorrado horas y la complicación adicional.


Creo que las cosas empiezan a complicarse si intentas modificar manualmente las clases generadas por EF.

Hay dos opciones que sugeriría seguir:

  1. No modifique la propiedad existente, sino agregue una nueva a su clase parcial, CreatedOnUTC o algo similar.
  2. Modifique la plantilla T4 para modificar la forma en que genera los accesadores para estas propiedades de fecha (más fácil si todas las propiedades DateTime funcionan de la misma manera). No será trivial, ya que depende del tipo, pero al menos le permitirá usar el generador en el futuro.

EF puede ser bastante desagradable a veces cuando se trata de cosas que genera.
En las aplicaciones de Database First cuando encuentro un problema similar, generalmente sigo este enfoque:

  • Deje propiedades autogeneradas pero conviértalas en private y cambie sus nombres;
  • Agregue propiedades públicas de "envoltura" que tengan sentido para el código comercial.

Por ejemplo, cambiaría el nombre de CreatedOn a algo más, como CreatedOnInternal (créditos a Jeff ) y lo marcaría como privado en el diseñador . En la clase parcial, agregaría una propiedad pública de contenedor CreatedOn que hace la conversión de ida y vuelta.


Sé que esta es una vieja pregunta, pero aquí hay una solución que no requiere reflexión o para editar el DbContext para cada propiedad nueva.

Consiste en editar el Context.tt :

Primero agregue lo siguiente usando sobre el archivo tt (alrededor de la línea 40):

using System.Data.Entity.Core.Objects;

Luego, justo debajo del constructor (alrededor de la línea 86 en mi caso), agregue el siguiente código de generación:

<# var entitiesWithDateTime = typeMapper.GetItemsToGenerate<EntityType>(itemCollection) .Where(e => e.Properties.Any(p => typeMapper.GetTypeName(p.TypeUsage) == "System.DateTime")); if(entitiesWithDateTime.Count() > 0) { #> private void ObjectMaterialized(object sender, ObjectMaterializedEventArgs e) { <# var count = 0; foreach (var entityType in entitiesWithDateTime) { #> <#=count != 0 ? "else " : String.Empty#>if(e.Entity is <#=entityType.Name#>) { var entity = e.Entity as <#=entityType.Name#>; <# foreach(var property in entityType.Properties.Where(p => typeMapper.GetTypeName(p.TypeUsage) == "System.DateTime")) { #> entity.<#=property.Name#> = DateTime.SpecifyKind(entity.<#=property.Name#>, DateTimeKind.Utc); <# } #> } <# count++; } #> } <# } #>

Esto iterará en tiempo de compilación alrededor de todas las entidades de DbContext y llamará a DateTime.SpecifyKind en cada DateTimeProperties.

Este código generará el mismo código que michael.aird pero sin requerir edición manual para cada propiedad nueva.


Un enfoque diferente es conectar el evento ObjectMaterialized en el DbContext y establecer el tipo allí.

En mi constructor de DbContext, hago esto:

((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += new ObjectMaterializedEventHandler(ObjectMaterialized);

y luego el método se ve así:

private void ObjectMaterialized(object sender, ObjectMaterializedEventArgs e) { Person person = e.Entity as Person; if (person != null) // the entity retrieved was a Person { if (person.BirthDate.HasValue) { person.BirthDate = DateTime.SpecifyKind(person.BirthDate.Value, DateTimeKind.Utc); } person.LastUpdatedDate = DateTime.SpecifyKind(person.LastUpdatedDate, DateTimeKind.Utc); person.EnteredDate = DateTime.SpecifyKind(person.EnteredDate, DateTimeKind.Utc); } }

El inconveniente es que debe asegurarse de configurarlo para cada propiedad que le interese, pero al menos se establece en el nivel más bajo posible.


Usé el mismo enfoque que Michael, solo entonces me zambullí un poco más, y usé el reflejo para buscar DateTime y DateTime.

Mi solución para garantizar que todos los valores de DateTime se muestren como Utc DateTimes es la siguiente:

Primero escribí tres métodos que están en mi clase de método DbContext Extensions. Porque necesito usarlo para múltiples DbContexts

public static void ReadAllDateTimeValuesAsUtc(this DbContext context) { ((IObjectContextAdapter)context).ObjectContext.ObjectMaterialized += ReadAllDateTimeValuesAsUtc; } private static void ReadAllDateTimeValuesAsUtc(object sender, ObjectMaterializedEventArgs e) { //Extract all DateTime properties of the object type var properties = e.Entity.GetType().GetProperties() .Where(property => property.PropertyType == typeof (DateTime) || property.PropertyType == typeof (DateTime?)).ToList(); //Set all DaetTimeKinds to Utc properties.ForEach(property => SpecifyUtcKind(property, e.Entity)); } private static void SpecifyUtcKind(PropertyInfo property, object value) { //Get the datetime value var datetime = property.GetValue(value, null); //set DateTimeKind to Utc if (property.PropertyType == typeof(DateTime)) { datetime = DateTime.SpecifyKind((DateTime) datetime, DateTimeKind.Utc); } else if(property.PropertyType == typeof(DateTime?)) { var nullable = (DateTime?) datetime; if(!nullable.HasValue) return; datetime = (DateTime?)DateTime.SpecifyKind(nullable.Value, DateTimeKind.Utc); } else { return; } //And set the Utc DateTime value property.SetValue(value, datetime, null); }

Y luego voy al constructor de mi WebsiteReadModelContext que es un objeto DbContext y llamo al método ReadAllDateTimeValuesAsUtc

public WebsiteReadModelContext() { this.ReadAllDateTimeValuesAsUtc(); }


Usaría edmx y especificaría otro nombre para la propiedad CreatedOn (como CreatedOnInternal). A continuación, configure el modificador de acceso para generación en Interno en lugar de Público. Luego puede implementar su propiedad personalizada en la clase parcial y no preocuparse por esto.