Obliczona kolumna w kodzie EF pierwsza

Muszę mieć jedną kolumnę w mojej bazie danych obliczoną przez bazę danych jako (suma wierszy) - (suma wierszy). Używam modelu code-first do tworzenia bazy danych.

Oto co mam na myśli:

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

Jak mogę to osiągnąć w EF CodeFirst?

Author: ANeves thinks SE is evil, 2013-03-23

7 answers

Możesz tworzyć obliczone kolumny w tabelach bazy danych. W modelu EF wystarczy adnotować odpowiednie właściwości atrybutem DatabaseGenerated:

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

Lub z płynnym odwzorowaniem:

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

Zgodnie z sugestią Matija Grcic i w komentarzu, dobrym pomysłem jest utworzenie właściwości private set, ponieważ prawdopodobnie nigdy nie chciałbyś ustawić jej w kodzie aplikacji. Entity Framework nie ma problemów z prywatnymi seterami.

Uwaga: dla EF. Net Core you należy użyć ValueGeneratedOnAddOrUpdate ponieważ HasDatabaseGeneratedOption nie istnieje, np.:

modelBuilder.Entity<Income>().Property(t => t.Summ)
    .ValueGeneratedOnAddOrUpdate()
 138
Author: Gert Arnold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-25 15:40:41
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 */ }
    }
}

Bibliografia:

 35
Author: Matija Grcic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-05 17:48:27

Od 2019 r. EF core pozwala na obliczanie kolumn w czysty sposób za pomocą płynnego API:

Załóżmy, że DisplayName jest kolumną obliczeniową, którą chcesz zdefiniować, musisz zdefiniować właściwość jak zwykle, ewentualnie z dostępem do własności prywatnej, aby zapobiec przypisaniu jej

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

Następnie, w konstruktorze modelu, zaadresuj go za pomocą definicji kolumny:

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

Aby uzyskać więcej informacji, spójrz na MSDN.

 14
Author: Yennefer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-18 18:27:01

W EF6 można po prostu skonfigurować ustawienie mapowania tak, aby ignorowało obliczoną właściwość, jak to:

Zdefiniuj obliczenia na własności get Twojego modelu:

public class Person
{
    // ...
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string FullName => $"{FirstName} {LastName}";
}

Następnie ustaw go na ignorowanie w konfiguracji modelu

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    //...
    modelBuilder.Entity<Person>().Ignore(x => x.FullName)
}
 3
Author: Fernando Vieira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-06 12:00:30

Jednym sposobem jest zrobienie tego z 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;

Możesz to zrobić w swoim obiekcie POCO public class FirstTable, ale nie sugerowałbym tego, ponieważ myślę, że to nie jest dobry projekt.

Innym sposobem byłoby użycie widoku SQL. Możesz odczytać widok podobny do tabeli z ramką encji. A w kodzie widoku możesz wykonywać obliczenia lub cokolwiek chcesz. Po prostu utwórz widok podobny do

-- 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
 1
Author: Linus Caldwell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-23 10:10:04

Zrobiłbym to używając tylko modelu widoku. Na przykład zamiast mieć klasę FirstTable jako jednostkę db, czy nie byłoby lepiej po prostu mieć klasę modelu widoku o nazwie FirstTable, a następnie mieć funkcję, która jest używana do zwracania tej klasy, która zawierałaby obliczoną sumę? Na przykład twoja klasa będzie po prostu:

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

I wtedy masz funkcję, którą wywołujesz, która zwraca obliczoną sumę:

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

Zasadniczo to samo co Widok SQL i jako @Linus wspomniałem, że nie sądzę, że byłoby dobrym pomysłem utrzymanie obliczonej wartości w bazie danych. Tylko kilka myśli.

 0
Author: craigvl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-16 08:54:06

Natknąłem się na to pytanie, próbując mieć Kod EF pierwszy model z kolumną łańcuchową "Slug" , być wyprowadzone z innej kolumny łańcuchowej "Nazwa". Podejście, które przyjęłam, było nieco inne, ale wyszło dobrze, więc podzielę się nim tutaj.

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

To, co jest miłe w tym podejściu, to automatyczne generowanie slug, nigdy nie ujawniając settera slug. The .Metoda ToUrlSlug () nie jest ważną częścią tego postu, możesz użyć wszystkiego na swoim miejscu, aby wykonać pracę, której potrzebujesz załatwione. Zdrowie!

 -2
Author: Patrick Michalina,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-30 23:53:29