Mit einer partiellen Klasse eine Immobilie innerhalb von LINQ-Anweisung
Ich versuche herauszufinden, der beste Weg, das zu tun, was ich dachte, dass es einfach sein würde.
Ich habe eine Datenbank-Modell mit der Bezeichnung Zeile stellt eine Zeile in einer Rechnung.
Sieht es in etwa so:
public partial class Line
{
public Int32 Id { get; set; }
public Invoice Invoice { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public Decimal Price { get; set; }
public Int32 Quantity { get; set; }
}
Dieser Klasse erzeugt wird aus dem db-Modell.
Ich habe eine Klasse, die fügt eine weitere Eigenschaft:
public partial class Line
{
public Decimal Total
{
get
{
return this.Price * this.Quantity
}
}
}
Nun, aus meiner Kunden-controller möchte ich etwas wie das hier tun:
var invoices = ( from c in _repository.Customers
where c.Id == id
from i in c.Invoices
select new InvoiceIndex
{
Id = i.Id,
CustomerName = i.Customer.Name,
Attention = i.Attention,
Total = i.Lines.Sum( l => l.Total ),
Posted = i.Created,
Salesman = i.Salesman.Name
}
)
Aber ich kann nicht, Dank der berüchtigten
The specified type member 'Total' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Was ist der beste Weg, um umgestalten, damit es funktioniert?
Habe ich versucht LinqKit, ich.Linien.AsEnumerable(), und setzen ich.Zeilen in mein InvoiceIndex Modell und berechnen Sie die Summe, die für die Ansicht.
Dass Letzte Lösung 'funktioniert', aber ich kann nicht sortiert, die Daten. Was möchte ich in der Lage sein zu tun ist
var invoices = ( from c in _repository.Customers
...
).OrderBy( i => i.Total )
Will auch ich auf der Seite meine Daten, so dass ich nicht wollen, Zeit zu verschwenden konvertieren die gesamte c.Rechnungen um eine Liste mit .AsEnumerable()
Bounty
Ich weiß, das muss ein sehr großes problem für einige Menschen. Nach Stunden der scheuern im internet habe ich zu dem Schluss gekommen, dass kein happy Abschluss gemacht wurde. Aber ich glaube, das muss ein Recht häufiges Hindernis für diejenigen, die versuchen zu tun, paging und Sortieren mit ASP MVC.
Ich verstehe, dass die Eigenschaft kann nicht zugeordnet werden von sql und daher kann man nicht Sortieren, bevor paging, aber das, was ich Suche, ist ein Weg, um mein gewünschtes Ergebnis.
Anforderungen für eine perfekte Lösung:
- TROCKEN, was bedeutet, meine gesamten Berechnungen existieren würde, in 1 Ort
- Unterstützung von sowohl Sortier-und paging -, und zwar in dieser Reihenfolge
- Ziehen Sie nicht die gesamte Tabelle von Daten in den Speicher mit .AsEnumerable oder .AsArray
Was ich wäre wirklich glücklich, zu finden, ist ein Weg zu geben Sie die Linq to entities-SQL in meinem erweiterten partiellen Klasse. Aber mir wurde gesagt, dies ist nicht möglich. Beachten Sie, dass eine Lösung muss nicht direkt die Gesamte Immobilie. Der Aufruf der Eigenschaft von IQueryable wird gar nicht unterstützt. Ich bin auf der Suche nach einem Weg, um das gleiche Ergebnis zu erzielen über eine andere Methode, doch ebenso einfach und orthogonal.
Der Gewinner der Prämie wird die Lösung mit der höchsten Stimmenzahl am Ende, es sei denn, jemand stellt eine perfekte Lösung 🙂
Ignorieren unten, es sei denn, bis Sie Lesen die Antwort(en):
{1}
Mit Jacek Lösung ich nahm es einen Schritt weiter und machte den Eigenschaften aufrufbarer mit LinqKit. Diese Weise auch die .AsQueryable().Sum() ist eingeschlossen in unsere partielle Klassen. Hier einige Beispiele von dem, was ich jetzt mache:
public partial class Line
{
public static Expression<Func<Line, Decimal>> Total
{
get
{
return l => l.Price * l.Quantity;
}
}
}
public partial class Invoice
{
public static Expression<Func<Invoice, Decimal>> Total
{
get
{
return i => i.Lines.Count > 0 ? i.Lines.AsQueryable().Sum( Line.Total ) : 0;
}
}
}
public partial class Customer
{
public static Expression<Func<Customer, Decimal>> Balance
{
get
{
return c => c.Invoices.Count > 0 ? c.Invoices.AsQueryable().Sum( Invoice.Total ) : 0;
}
}
}
Erste trick war das .Zählung überprüft. Diese sind notwendig, weil ich denke, das kann man nicht nennen .AsQueryable auf eine leere Menge. Sie erhalten eine Fehlermeldung über Null Materialisierung.
Mit diesen 3 partiellen Klassen angelegt können Sie nun tun tricks, wie
var customers = ( from c in _repository.Customers.AsExpandable()
select new CustomerIndex
{
Id = c.Id,
Name = c.Name,
Employee = c.Employee,
Balance = Customer.Balance.Invoke( c )
}
).OrderBy( c => c.Balance ).ToPagedList( page - 1, PageSize );
var invoices = ( from i in _repository.Invoices.AsExpandable()
where i.CustomerId == Id
select new InvoiceIndex
{
Id = i.Id,
Attention = i.Attention,
Memo = i.Memo,
Posted = i.Created,
CustomerName = i.Customer.Name,
Salesman = i.Salesman.Name,
Total = Invoice.Total.Invoke( i )
} )
.OrderBy( i => i.Total ).ToPagedList( page - 1, PageSize );
Sehr cool.
Gibt es einen Haken, LinqKit unterstützt nicht den Aufruf von Eigenschaften, erhalten Sie eine Fehlermeldung, um den Versuch zu casten PropertyExpression zu LambaExpression. Es gibt 2 Möglichkeiten das zu umgehen. Erstens durch das ziehen der Ausdruck, der sich wie so
var tmpBalance = Customer.Balance;
var customers = ( from c in _repository.Customers.AsExpandable()
select new CustomerIndex
{
Id = c.Id,
Name = c.Name,
Employee = c.Employee,
Balance = tmpBalance.Invoke( c )
}
).OrderBy( c => c.Balance ).ToPagedList( page - 1, PageSize );
denen ich dachte, war irgendwie albern. Also modifizierte ich LinqKit zu ziehen, die bekommen {} - Wert, wenn es auf eine Eigenschaft. Die Art und Weise es arbeitet auf der Ausdruck ist ähnlich der Reflexion, so dass Ihr nicht wie der compiler vorgeht, um Kunden lösen.Balance für uns. Es ist ein 3-Zeile ändern, das ich TransformExpr in ExpressionExpander.cs. Wahrscheinlich nicht der sicherste code und brechen könnte andere Dinge, aber es funktioniert jetzt und ich habe informiert der Autor über den Mangel.
Expression TransformExpr (MemberExpression input)
{
if( input.Member is System.Reflection.PropertyInfo )
{
return Visit( (Expression)( (System.Reflection.PropertyInfo)input.Member ).GetValue( null, null ) );
}
//Collapse captured outer variables
if( input == null
In der Tat habe ich so ziemlich garantieren, dass dieser code brechen einige Dinge, aber es funktioniert im moment und das ist gut genug. 🙂
- Ich denke, dass Ihr letztes update sollte wirklich seine eigene Antwort.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Gibt es eine andere Möglichkeit, die ist ein bisschen komplexer, aber es gibt Ihnen die Fähigkeit zum Kapseln von dieser Logik.
Dann die Abfrage so umschreiben,
Es für mich funktionierte, führt Abfragen auf server-Seite und stimmt mit der Regel TROCKEN.
AsQueryable
nicht möglich sein würde, in eine LINQ to Entities-Projektion. Aber es funktioniert (gerade getestet). Halten Sie diese schönen "trick" in den Sinn.Ihre zusätzliche Eigenschaft ist einfach eine Berechnung der Daten aus dem Modell, aber die Eigenschaft ist nicht etwas, EF kann natürlich übersetzen. SQL ist durchaus in der Lage, die Durchführung dieser Rechenoperationen und EF kann übersetzen die Berechnung. Verwenden Sie es anstelle der Eigenschaft, wenn Sie es brauchen, in Ihrer Abfrage.
Probieren Sie etwas wie dieses:
Grundsätzlich das Abfragen der Datenbank für die Datensätze, die Sie brauchen, bringt Sie in den Speicher und dann funktioniert die Summierung sobald die Daten vorhanden sind.
ToArray
/AsEnumerable
nennen?Ich denke einfachste Weg, um dieses problem zu beheben ist die Verwendung DelegateDecompiler.Entity Framework (durch Alexander Zaytsev)
Es ist eine Bibliothek, die in der Lage ist, zu dekompilieren ein Delegierter oder eine Methode, die Körper, um seine lambda-Darstellung.
Erklären:
Davon ausgehen, haben wir eine Klasse mit einer berechneten Eigenschaft
Und du wirst Abfrage-Mitarbeiter, indem Sie Ihre vollständigen Namen
Wenn Sie anrufen .Dekompilieren Methode es decompiles Ihr berechnete Eigenschaften Ihrer zugrunde liegenden Darstellung und die Abfrage wird anderes die folgende Abfrage
Wenn Ihre Klasse nicht über ein [Berechnet] - Attribut verwenden, können Sie die .Berechnet () - Erweiterungsmethode..
Außerdem können Sie Methoden aufrufen, die ein einzelnes Element (Any, Count, First, Single, etc), sowie andere Methoden in identischer Weise wie diese:
Wieder, die FullName-Eigenschaft wird dekompiliert:
Async-Unterstützung mit EntityFramework
Den
DelegateDecompiler.EntityFramework
Paket bietetDecompileAsync
Erweiterung Methode, die Unterstützung für EF-s Asynchrone Operationen.Zusätzlich
Finden Sie 8-Wege-mischen einige Eigenschaftswerte zusammen, die in
EF
hier:Berechnete Eigenschaften und Entity Framework. (geschrieben von Dave Glick)
Zwar nicht eine Antwort auf dein Thema, es ist vielleicht eine Antwort auf dein problem:
Da Sie scheinen bereit, um die Datenbank ändern, warum nicht erstellen eine neue Ansicht in der Datenbank, die im Grunde ist
und ändern Sie dann Ihre Daten-Modell zu verwenden TotalledLine statt LineTable?
Nach allem, wenn Ihre Anwendung benötigt werden, Insgesamt, es ist nicht weit hergeholt zu denken, andere könnten, und die Zentralisierung, die Art der Berechnung in die Datenbank ist ein Grund für die Verwendung einer Datenbank.
Nur hinzufügen, dass einige meiner eigenen Gedanken hier. Denn ich bin ein Perfektionist ich will nicht zu ziehen den gesamten Inhalt der Tabelle in den Speicher nur so kann ich die Berechnung dann Sortieren Sie dann die Seite. Also irgendwie die Tabelle muss wissen, was die Summe ist.
Was ich dachte, war es, erstellen Sie ein neues Feld in der Zeile Tabelle namens "CalcTotal' enthält die berechnete Summe der Zeile. Dieser Wert festgelegt werden jedes mal, wenn die Zeile geändert wird, basierend auf dem Wert .Insgesamt
Dies hat 2 Vorteile. Erstens, kann ich die Abfrage ändern, die zu diesem:
Sortierung und paging funktioniert, weil es ein db-Feld. Und ich kann ein Häkchen (- Linie.CalcTotal == Linie.Gesamt ) in meinem controller zu erkennen und tom foolery. Es tut verursachen ein wenig Aufwand in meinem repository, das heißt, wenn ich gehe um zu speichern, oder erstellen Sie eine neue Zeile ein, die ich hätte hinzufügen
aber ich denke, es kann es Wert sein.
Warum gehen Sie zu der Mühe, mit 2 Eigenschaften, die auf die gleichen Daten?
Nun, es ist wahr, ich konnte
in meinem repository. Wäre es nicht sehr orthogonal, aber, und wenn Sie einige änderungen Zeile Summen geschehen musste, es würde viel mehr Sinn zu Bearbeiten, die teilweise Line-Klasse als die Line-repository.