Necesito tener una columna en mi base de datos calculada por la base de datos como (suma de filas) - (suma de filasb). Estoy usando el modelo de código primero para crear mi base de datos.
Esto es lo que quiero decir:
public class Income {
[Key]
public int UserID { get; set; }
public double inSum { get; set; }
}
public class Outcome {
[Key]
public int UserID { get; set; }
public double outSum { get; set; }
}
public class FirstTable {
[Key]
public int UserID { get; set; }
public double Sum { get; set; }
// This needs to be calculated by DB as
// ( Select sum(inSum) FROM Income WHERE UserID = this.UserID)
// - (Select sum(outSum) FROM Outcome WHERE UserID = this.UserID)
}
¿Cómo puedo lograr esto en EF CodeFirst?
ValueGeneratedOnAddOrUpdate()
porqueHasDatabaseGeneratedOption
no existe. De lo contrario, gran respuesta.public string ChargePointText { get; set; } public class FirstTable { [Key] public int UserID { get; set; } [DatabaseGenerated(DatabaseGeneratedOption.Computed)] public string Summ { get { return /* do your sum here */ } private set { /* needed for EF */ } } }
Referencias:
fuente
/* do your sum here */
no se aplica. Si la propiedad se calcula dentro de la clase, debe anotarse como[NotMapped]
. Pero el valor proviene de la base de datos, por lo que debería ser unaget
propiedad simple .A partir de 2019, EF core le permite tener columnas calculadas de una manera limpia con la API fluida:
Suponga que
DisplayName
es la columna calculada que desea definir, debe definir la propiedad como de costumbre, posiblemente con un acceso de propiedad privada para evitar asignarlapublic class Person { public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } // this will be computed public string DisplayName { get; private set; } }
Luego, en el generador de modelos, abórdelo con la definición de columna:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Person>() .Property(p => p.DisplayName) // here is the computed query definition .HasComputedColumnSql("[LastName] + ', ' + [FirstName]"); }
Para obtener más información, consulte MSDN .
fuente
En EF6, puede configurar la configuración de mapeo para ignorar una propiedad calculada, como esta:
Defina el cálculo en la propiedad get de su modelo:
public class Person { // ... public string FirstName { get; set; } public string LastName { get; set; } public string FullName => $"{FirstName} {LastName}"; }
Luego configúrelo para ignorar en la configuración del modelo
protected override void OnModelCreating(ModelBuilder modelBuilder) { //... modelBuilder.Entity<Person>().Ignore(x => x.FullName) }
fuente
Una forma es hacerlo con LINQ:
var userID = 1; // your ID var income = dataContext.Income.First(i => i.UserID == userID); var outcome = dataContext.Outcome.First(o => o.UserID == userID); var summ = income.inSumm - outcome.outSumm;
Puede hacerlo dentro de su objeto POCO
public class FirstTable
, pero no sugeriría hacerlo, porque creo que no es un buen diseño.Otra forma sería usar una vista SQL. Puede leer una vista como una tabla con Entity Framework. Y dentro del código de vista, puede hacer cálculos o lo que quiera. Simplemente crea una vista como
-- not tested SELECT FirstTable.UserID, Income.inCome - Outcome.outCome FROM FirstTable INNER JOIN Income ON FirstTable.UserID = Income.UserID INNER JOIN Outcome ON FirstTable.UserID = Outcome.UserID
fuente
Haría esto simplemente usando un modelo de vista. Por ejemplo, en lugar de tener la clase FirstTable como una entidad db, ¿no sería mejor tener una clase de modelo de vista llamada FirstTable y luego tener una función que se usa para devolver esta clase que incluiría la suma calculada? Por ejemplo, su clase sería simplemente:
public class FirstTable { public int UserID { get; set; } public double Sum { get; set; } }
Y luego tendrías una función a la que llamas que devuelve la suma calculada:
public FirsTable GetNetSumByUserID(int UserId) { double income = dbcontext.Income.Where(g => g.UserID == UserId).Select(f => f.inSum); double expenses = dbcontext.Outcome.Where(g => g.UserID == UserId).Select(f => f.outSum); double sum = (income - expense); FirstTable _FirsTable = new FirstTable{ UserID = UserId, Sum = sum}; return _FirstTable; }
Básicamente lo mismo que una vista SQL y como mencionó @Linus, no creo que sea una buena idea mantener el valor calculado en la base de datos. Solo algunos pensamientos.
fuente
I don't think it would be a good idea keeping the computed value in the database
, especialmente si va a usar Azure SQL, que comenzará a bloquearse con una carga pesada.Me encontré con esta pregunta cuando intentaba tener un modelo EF Code First con una columna de cadena "Slug", derivado de otra columna de cadena "Nombre". El enfoque que tomé fue ligeramente diferente pero funcionó bien, así que lo compartiré aquí.
private string _name; public string Name { get { return _name; } set { _slug = value.ToUrlSlug(); // the magic happens here _name = value; // but don't forget to set your name too! } } public string Slug { get; private set; }
Lo bueno de este enfoque es que obtienes la generación automática de babosas, sin exponer nunca al colocador de babosas. El método .ToUrlSlug () no es la parte importante de esta publicación, puede usar cualquier cosa en su lugar para hacer el trabajo que necesita. ¡Salud!
fuente
Slug
aName
? Como está escrito actualmente, elName
setter ni siquiera debería compilar.