รองรับเฉพาะ initializers สมาชิกเอนทิตีและคุณสมบัติการนำทางของเอนทิตีเท่านั้น


102

ฉันได้รับข้อยกเว้นนี้:

ไม่สนับสนุนสมาชิกประเภท 'ชำระเงิน' ที่ระบุใน LINQ ถึงเอนทิตี รองรับเฉพาะ initializers สมาชิกเอนทิตีและคุณสมบัติการนำทางของเอนทิตีเท่านั้น

    public ActionResult Index()
    {
        var debts = storeDB.Orders
            .Where(o => o.Paid == false)
            .OrderByDescending(o => o.DateCreated);

        return View(debts);
    }

คลาส My Model

public partial class Order
{
    public bool Paid {
        get {
            return TotalPaid >= Total;
        }
    }

    public decimal TotalPaid {
        get {
            return Payments.Sum(p => p.Amount);
        }
    }

การชำระเงินเป็นตารางที่เกี่ยวข้องซึ่งมีจำนวนเงินในฟิลด์ข้อความค้นหาจะทำงานถ้าฉันลบส่วนคำสั่ง Where ที่แสดงข้อมูลที่ถูกต้องเกี่ยวกับการชำระเงินมีเงื่อนงำอะไรที่ผิดกับรหัส

แก้ไขเช่นเดียวกับคำตอบที่แนะนำด้วย:

    public ActionResult Index()
    {
        var debts = storeDB.Orders
            .OrderByDescending(o => o.DateCreated)
            .ToList()
            .Where(o => o.Paid == false);

        return View(debts);
    }

15
คำตอบง่ายๆ: คุณไม่สามารถใช้คุณสมบัติที่ไม่ได้แมปในแบบสอบถาม linq-to-entities! คุณสมบัติที่แมปเท่านั้นที่จะถูกแปลเป็น SQL
Ladislav Mrnka

คำตอบ:


114

เอนทิตีพยายามแปลงคุณสมบัติแบบชำระเงินของคุณเป็น SQL และไม่สามารถทำได้เนื่องจากไม่ได้เป็นส่วนหนึ่งของสคีมาของตาราง

สิ่งที่คุณทำได้คือปล่อยให้เอนทิตีสืบค้นตารางโดยไม่มีตัวกรองแบบชำระเงินแล้วกรองสิ่งที่ไม่ได้ชำระเงินออก

public ActionResult Index()
{
    var debts = storeDB.Orders
        //.Where(o => o.Paid == false)
        .OrderByDescending(o => o.DateCreated);

    debts = debts.Where(o => o.Paid == false);

    return View(debts);
}

แน่นอนว่าหมายความว่าคุณนำข้อมูลทั้งหมดกลับไปที่เว็บเซิร์ฟเวอร์และกรองข้อมูล หากคุณต้องการกรองบนเซิร์ฟเวอร์ DB คุณสามารถสร้างคอลัมน์จากการคำนวณบนตารางหรือใช้ Stored Procedure


25

เพียงแค่ต้องแก้ปัญหาที่คล้ายกัน วิธีแก้ปัญหาข้างต้นจำเป็นต้องมีการประมวลผลในหน่วยความจำซึ่งเป็นแนวทางปฏิบัติที่ไม่ดี (ขี้เกียจโหลด)

วิธีแก้ปัญหาของฉันคือเขียนตัวช่วยที่ส่งคืนเพรดิเคต:

public static class Extensions
{
    public static Expression<Func<Order, bool>> IsPaid()
    {
        return order => order.Payments.Sum(p => p.Amount) >= order.Total;
    }
}

คุณสามารถเขียนคำสั่ง linq ของคุณใหม่เป็น:

var debts = storeDB.Orders
                    .Where(Extensions.IsPaid())
                    .OrderByDescending(o => o.DateCreated);

สิ่งนี้มีประโยชน์เมื่อคุณต้องการใช้ตรรกะการคำนวณซ้ำ (DRY) ข้อเสียคือตรรกะไม่ได้อยู่ในโมเดลโดเมนของคุณ


1
มีจำนวนของห้องสมุดที่พยายามที่จะทำให้วิธีนี้มากกว่า "สร้างขึ้นใน" มีความเห็น: stackoverflow.com/a/27383641/470183 Linq-to-entities จำกัด เฉพาะนิพจน์ที่ใช้ "Canonical Functions" ซึ่งสามารถเปลี่ยนเป็น SQL ได้ C # 6 แนะนำ "Expression bodied functions" แต่สิ่งเหล่านี้ไม่ใช่ lambdas ที่แท้จริง (ดู: stackoverflow.com/a/28411444/470183 ) ยังคงเป็นการดีที่จะมีสิ่งนี้ในกรอบดังนั้น WIBNI data.uservoice.com/forums/…
James Close

1
Expression<Func<xx,yy>>ขอขอบคุณสำหรับตัวอย่างนี้ง่ายและกระชับ ฉันเคยเข้าใจมาก่อน แต่ตอนนี้ดูเหมือนชัดเจนแล้ว
AlexB

17

ปัญหานี้อาจมาจาก[NotMapped]คุณสมบัติที่มีชื่อเดียวกันใน DB Model และ View Model ของคุณ

AutoMapper พยายามเลือกจากฐานข้อมูลระหว่างการฉายภาพ และคุณสมบัติ NotMapped ไม่มีอยู่ในฐานข้อมูลอย่างชัดเจน

วิธีแก้ปัญหาคือIgnoreคุณสมบัติในการกำหนดค่า AutoMapper เมื่อแมปจาก DB Model ไปยัง View Model

  1. มองหา[NotMapped]คุณสมบัติที่มีชื่อFooใน DB Model ของคุณ
  2. มองหาคุณสมบัติที่มีชื่อเดียวกันFooใน View Model ของคุณ
  3. หากเป็นเช่นนั้นให้เปลี่ยนการกำหนดค่า AutoMapper ของคุณ เพิ่ม.ForMember(a => a.Foo, b => b.Ignore());

Dang AutoMapper Projection จับฉันด้วยขอบคุณสำหรับคำตอบ!
Chase Florell

15

Linq แปลงคำสั่งเป็นคำสั่ง SQL และดำเนินการลงในฐานข้อมูล

ตอนนี้การแปลงนี้เกิดขึ้นเฉพาะกับสมาชิกเอนทิตีตัวเริ่มต้นและคุณสมบัติการนำทางของเอนทิตี ดังนั้นเพื่อให้บรรลุฟังก์ชันหรือได้รับการเปรียบเทียบคุณสมบัติเราต้องแปลงเป็นรายการในหน่วยความจำก่อนจากนั้นจึงใช้ฟังก์ชันเพื่อดึงข้อมูล

ดังนั้นในผลรวม

var debts = storeDB.Orders.toList()
        .Where(o => o.Paid == false)
        .OrderByDescending(o => o.DateCreated);

21
ฉันขอแนะนำว่าการขอให้ใครบางคนทำ toList () ตามคำสั่งซื้อนั้นอันตรายเนื่องจากจะหมายถึงการดึงรายการทั้งหมด
elgrego

นี่เป็นสิ่งที่ดีสำหรับฉันเพราะคุณสมบัติที่มีปัญหาของฉันอยู่ในฟังก์ชัน Sum Linq ไม่อยู่ในประโยค Where ดังนั้นฉันจึงไม่ได้รับข้อมูลที่ไม่จำเป็นและข้อมูลที่ถูกดึงกลับมาฉันกำลังใช้ฟังก์ชัน Linq Sum ซึ่งทำงานในรายการ ขอบคุณ! สิ่งที่อาจดูไม่ดีในตอนแรกสามารถช่วยได้มากในบางสถานการณ์!
Dov Miller

11

สาเหตุที่เป็นไปได้อื่น ๆ เป็นเพราะคุณใช้IEnumerableเพื่อทรัพย์สินของคุณแทนที่จะเป็นICollection

แทนที่จะเป็น:

public class This
{
    public long Id { get; set; }
    //...
    public virtual IEnumerable<That> Thats { get; set; }
}

ทำเช่นนี้:

public class This
{
    public long Id { get; set; }
    //...
    public virtual ICollection<That> Thats { get; set; }
}

และคุณเป็นคนขี้อาย ... โง่ที่เสียเวลาไป 2 ชั่วโมงกว่า ๆ


2

สถานการณ์นี้อาจเกิดขึ้นได้หากคุณใช้ชนิด EntityFramework ที่ไม่สนับสนุนเช่น int ที่ไม่ได้ลงนาม

นี่เป็นกรณีของฉันที่เกิดข้อผิดพลาดดังกล่าว

ตรวจสอบข้อมูลเพิ่มเติมเกี่ยวกับประเภทที่รองรับ: https://msdn.microsoft.com/en-us/library/ee382832(v=vs.100).aspx

มีวิธีแก้ปัญหาบางอย่างสำหรับสถานการณ์ดังกล่าวอธิบายโดย GFoley83: จะใช้ประเภท int / long ที่ไม่ได้ลงนามกับ Entity Framework ได้อย่างไร


ลิงค์นี้ช่วยประหยัดเวลาได้มากทีเดียว! ขอบคุณมาก!
Vladimir Semashkin

0

ฉันประสบปัญหานี้เนื่องจากมีตัวแปรสมาชิกที่มีget without setคุณสมบัติเท่านั้น

นั่นหมายถึงมันauto calculatedและnot storedเป็นคอลัมน์ในthe table

ดังนั้นจึงอยู่not existในtable schema

เพื่อmake sureว่าตัวแปรสมาชิกnot auto calculatedเพื่อและคุณสมบัติhavegettersetter


-1

edmx และโมเดลบริบทของคุณมีคุณสมบัติที่แตกต่างกันซึ่งถูกเพิ่มเข้าไปใหม่ใน db

อัปเดต EDMX ของคุณรีเฟรชให้ถูกต้อง Bulid โครงการของคุณและเรียกใช้อีกครั้ง

จะช่วยแก้ปัญหาของคุณ

ขอแสดงความนับถือ Ganesh Nikam

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.