Muszę mieć jedną kolumnę w mojej bazie danych obliczoną przez bazę danych jako (suma wierszy) - (suma wierszyb). Do tworzenia mojej bazy danych używam modelu Code First.
Oto o co mi chodzi:
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?
ValueGeneratedOnAddOrUpdate()
ponieważHasDatabaseGeneratedOption
nie istnieje. W przeciwnym razie świetna odpowiedź.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:
źródło
/* do your sum here */
nie ma zastosowania. Jeśli właściwość jest obliczana wewnątrz klasy, powinna być oznaczona jako[NotMapped]
. Ale wartość pochodzi z bazy danych, więc powinna to być tylko prostaget
właściwość.Począwszy od 2019 r., EF core pozwala mieć obliczone kolumny w czysty sposób z Fluent API:
Załóżmy, że
DisplayName
jest to kolumna obliczeniowa, którą chcesz zdefiniować, musisz zdefiniować właściwość w zwykły sposób, prawdopodobnie za pomocą metody dostępu do właściwości prywatnej, aby zapobiec przypisywaniu jejpublic 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 modeli 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, zajrzyj na MSDN .
źródło
W EF6 możesz po prostu skonfigurować ustawienie mapowania, aby zignorować obliczoną właściwość, na przykład:
Zdefiniuj obliczenia na właściwości get swojego modelu:
public class Person { // ... public string FirstName { get; set; } public string LastName { get; set; } public string FullName => $"{FirstName} {LastName}"; }
Następnie ustaw go tak, aby ignorował konfigurację modelu
protected override void OnModelCreating(ModelBuilder modelBuilder) { //... modelBuilder.Entity<Person>().Ignore(x => x.FullName) }
źródło
Jednym ze sposobów jest zrobienie tego za pomocą 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ż uważam, że to nie jest dobry projekt.Innym sposobem byłoby użycie widoku SQL. Możesz czytać widok jak tabelę za pomocą Entity Framework. W kodzie widoku możesz wykonywać obliczenia lub cokolwiek chcesz. Po prostu stwórz taki widok
-- 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
źródło
Zajmę się tym, używając tylko modelu widoku. Na przykład zamiast klasy FirstTable jako jednostki db 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 wyglądałaby po prostu:
public class FirstTable { public int UserID { get; set; } public double Sum { get; set; } }
A potem miałbyś wywoływaną funkcję, 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 jak wspomniał @Linus, nie sądzę, aby dobrym pomysłem byłoby przechowywanie obliczonej wartości w bazie danych. Tylko kilka myśli.
źródło
I don't think it would be a good idea keeping the computed value in the database
- zwłaszcza jeśli zamierzasz używać Azure SQL, które zaczną blokować się przy dużym obciążeniu.Natknąłem się na to pytanie, próbując mieć model EF Code First z kolumną ciągu „Slug”, pochodzącą z innej kolumny ciągu „Name”. Podejście, które obrałem, 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 fajne w tym podejściu, to automatyczne generowanie informacji o kule, bez narażania ustawiacza. Metoda .ToUrlSlug () nie jest istotną częścią tego postu, możesz użyć w jej miejsce czegokolwiek, aby wykonać pracę, którą potrzebujesz. Twoje zdrowie!
źródło
Slug
sięName
? Jak obecnie napisano,Name
setter nie powinien się nawet kompilować.