web-dev-qa-db-fra.com

Colonne calculée dans le code EF en premier

Je dois avoir une colonne dans ma base de données calculée par base de données comme (somme de lignes) - (somme de lignesb) J'utilise un modèle de code d'abord pour créer ma base de données.

Voici ce que je veux dire:

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)
}

Comment puis-je réaliser cela dans EF CodeFirst?

65
CodeDemen

Vous pouvez créer colonnes calculées dans vos tables de base de données. Dans le modèle EF, vous devez simplement annoter les propriétés correspondantes avec l'attribut DatabaseGenerated:

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public double Summ { get; private set; } 

Ou avec cartographie fluide:

modelBuilder.Entity<Income>().Property(t => t.Summ)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed)

Comme suggéré par Matija Grcic et dans un commentaire, il est judicieux de définir la propriété private set, car vous ne voudrez probablement jamais la définir dans le code de l'application. Entity Framework n'a pas de problèmes avec les setters privés.

117
Gert Arnold
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 */ }
    }
}

Références: 

26
Matija Grcic

Une façon est de le faire avec 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;

Vous pouvez le faire dans votre objet POCO public class FirstTable, mais je ne le recommanderais pas, car je pense ce n'est pas un bon design.

Une autre façon serait d'utiliser une vue SQL. Vous pouvez lire une vue comme une table avec Entity Framework. Et dans le code de vue, vous pouvez faire des calculs ou faire ce que vous voulez. Il suffit de créer une vue comme

-- 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
2
Linus Caldwell

Je voudrais y remédier en utilisant simplement un modèle de vue. Par exemple, au lieu d'avoir la classe FirstTable en tant qu'entité de base de données, ne serait-il pas préférable d'avoir simplement une classe de modèle de vue appelée FirstTable et d'avoir ensuite une fonction utilisée pour renvoyer cette classe incluant la somme calculée? Par exemple, votre classe serait simplement: 

public class FirstTable {
  public int UserID { get; set; }
  public double Sum { get; set; }
 }

Et puis vous auriez une fonction que vous appelez qui retourne la somme calculée:

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;
}

Fondamentalement identique à une vue SQL et comme @Linus l'a mentionné, je ne pense pas que ce serait une bonne idée de conserver la valeur calculée dans la base de données. Juste quelques pensées.

1
craigvl

À partir de 2019, EF core vous permet d’avoir des colonnes calculées de manière propre avec l’API fluide:

Supposons que DisplayName soit la colonne calculée que vous souhaitez définir, vous devez définir la propriété comme d'habitude, éventuellement avec un accesseur de propriété privée pour empêcher son affectation.

public 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; }
}

Ensuite, dans le constructeur du modèle, adressez-le à l'aide de la définition de colonne:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .Property(p => p.DisplayName)
        // here is the computed query definition
        .HasComputedColumnSql("[LastName] + ', ' + [FirstName]");
}

Pour plus d'informations, consultez MSDN .

0
Pier