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.
InformationsquelleAutor Charles | 2011-07-29
Schreibe einen Kommentar