Apa aspek paling sulit atau paling disalahpahami dari LINQ? [Tutup]


282

Latar belakang: Selama bulan depan, saya akan memberikan tiga ceramah tentang atau setidaknya termasuk LINQdalam konteks C#. Saya ingin tahu topik mana yang layak memberikan perhatian yang cukup, berdasarkan pada apa yang sulit dipahami orang, atau apa yang mereka anggap keliru. Saya tidak akan secara khusus berbicara tentang LINQke SQLatau Entity Framework kecuali sebagai contoh bagaimana query dapat dijalankan dari jarak jauh menggunakan pohon ekspresi (dan biasanya IQueryable).

Jadi, apa yang menurut Anda sulit LINQ? Apa yang Anda lihat dalam hal kesalahpahaman? Contohnya mungkin salah satu dari yang berikut, tapi tolong jangan membatasi diri Anda sendiri!

  • Bagaimana C#kompiler memperlakukan ekspresi kueri
  • Ekspresi Lambda
  • Pohon ekspresi
  • Metode penyuluhan
  • Jenis anonim
  • IQueryable
  • Deferred vs eksekusi segera
  • Streaming vs eksekusi buffered (mis. OrderBy ditangguhkan tetapi buffered)
  • Variabel lokal yang diketik secara implisit
  • Membaca tanda tangan umum yang kompleks (mis. Enumerable.Join )

3
Saya akan tertarik untuk mengetahui kapan Anda akan melakukan pembicaraan ini, dan jika ada cara untuk melihatnya secara online
Mark Heath

2
Pembicaraan pertama: Kopenhagen, 30 Oktober. Semoga ini bisa direkam. (Seluruh hari!) Pembicaraan kedua: London, 19 November di malam hari, London .NET Users Group, mungkin di Push LINQ. Pembicaraan ketiga: Membaca, 22 November, Hari Pengembang Pengembang, Menerapkan LINQ ke Objek dalam 60 menit.
Jon Skeet

1
Downvoters: silakan tambahkan komentar penjelasan.
Jon Skeet

2
@ Jon, Maaf, tapi saya harus menutup ini.
Tim Post

3
@Tim: Cukup adil - toh itu tidak mendapatkan jawaban lagi. Secara pribadi saya pikir itu memang berakhir menjadi konstruktif, pikiran Anda - saya pasti menemukan itu berguna untuk melihat apa yang orang menemukan rumit. Saya mungkin tidak akan bertanya sekarang ...
Jon Skeet

Jawaban:


271

Eksekusi tertunda


12
Righto - ini jelas merupakan favorit di antara pembaca, yang merupakan hal paling penting untuk pertanyaan ini. Saya akan menambahkan "buffering vs streaming" ke dalam campuran juga, karena itu terkait erat - dan sering tidak dibahas sedetail yang ingin saya lihat di buku.
Jon Skeet

10
Betulkah? Saya sering menunjukkan sifat malas kepada saya saat belajar Linq, itu tidak pernah menjadi masalah bagi saya.
Adam Lassek

26
Setuju dengan ALassek. Dokumentasi MSDN dengan jelas menyatakan sifat evaluasi malas LINQ. Mungkin masalah sebenarnya adalah sifat pemrograman yang malas dari para pengembang ... =)
Seiti

4
... terutama ketika Anda menyadari bahwa itu berlaku untuk LINQ untuk objek dan bukan hanya LINQ 2 SQL - ketika Anda melihat 10 panggilan metode web untuk mengambil daftar item ketika Anda sudah melakukan enumerasi melalui daftar item yang sama dan Anda pikir daftar sudah dievaluasi
Simon_Weaver

5
Mengetahui apa pernyataan hasil dan cara kerjanya adalah IMHO penting untuk pemahaman menyeluruh tentang LINQ.
RASHIr

125

Saya tahu konsep eksekusi yang ditangguhkan harus dipukuli oleh saya sekarang, tetapi contoh ini benar-benar membantu saya mendapatkan pemahaman praktis tentang hal itu:

static void Linq_Deferred_Execution_Demo()
{
    List<String> items = new List<string> { "Bob", "Alice", "Trent" };

    var results = from s in items select s;

    Console.WriteLine("Before add:");
    foreach (var result in results)
    {
        Console.WriteLine(result);
    }

    items.Add("Mallory");

    //
    //  Enumerating the results again will return the new item, even
    //  though we did not re-assign the Linq expression to it!
    //

    Console.WriteLine("\nAfter add:");
    foreach (var result in results)
    {
        Console.WriteLine(result);
    }
}

Kode di atas mengembalikan yang berikut:

Before add:
Bob
Alice
Trent

After add:
Bob
Alice
Trent
Mallory

2
blogs.msdn.com/b/charlie/archive/2007/12/09/... <- Saya pikir ini adalah blog terbaik untuk menjelaskannya menurut pendapat saya. (jauh di luar 2007, tidak percaya sudah ada selama itu)
Phill

104

Bahwa ada lebih dari sekedar LINQuntuk SQLdan fitur lebih dari sekedar SQLparser yang tertanam dalam bahasa.


6
Saya muak dengan semua orang yang berpikir bahwa: /
TraumaPony

40
Tidak semua orang melakukannya! Saya masih tidak tahu apa itu LINQ to SQL, dan saya menggunakan LINQ sepanjang waktu.
Robert Rossney

2
Saya sangat kesal ketika saya mencoba dan menjelaskan sesuatu menggunakan LINQ dan orang lain hanya menatap saya dan berkata "ohhh saya tidak menggunakan LINQ untuk hal seperti itu, hanya SQL" :(
Nathan W

13
Setuju, banyak orang tampaknya tidak mengerti bahwa LINQ adalah alat tujuan umum.
Matthew Olenik

86

Notasi O besar . LINQ membuatnya sangat mudah untuk menulis algoritma O (n ^ 4) tanpa menyadarinya, jika Anda tidak tahu apa yang Anda lakukan.


16
Bagaimana dengan sebuah contoh?
hughdbrown

4
Sejauh contoh, mungkin maksudnya fakta bahwa sangat mudah untuk memiliki klausa Select berisi banyak operator Sum () yang masing-masing menyebabkan operan lain pada seluruh recordset.
Rob Packwood

1
Bahkan mungkin layak melalui apa notasi O besar dan mengapa itu penting, serta beberapa contoh pertanyaan yang dihasilkan tidak efisien. Saya pikir itulah yang disarankan oleh poster asli, tetapi saya pikir saya akan menyebutkannya. - EDIT: baru menyadari bahwa pos ini berumur 1,5 tahun :-)
zcrar70

7
Itu tidak akan menjadi O (n ^ x), itu akan menjadi O (xn), yang hanya O (n).
Malfist

3
Mencoba melakukan join tanpa join operator akan menghasilkan O (n ^ x): dari i1 di range1 dari i2 di range2 dari i3 di range3 dari i4 di range4 di mana i1 == i2 && i3 == i4 pilih new {i1, i2, i3, i4}. Dan saya benar-benar melihat ini ditulis sebelumnya. Ini bekerja, tetapi sangat lambat.
MarkPflug

55

Saya pikir fakta bahwa Lambdaekspresi dapat menyelesaikan baik pohon ekspresi maupun delegasi anonim, sehingga Anda dapat meneruskan lambdaekspresi deklaratif yang sama ke IEnumerable<T>metode IQueryable<T>ekstensi dan metode ekstensi.


2
Sepakat. Saya seorang veteran dan saya baru menyadari casting implisit ini terjadi ketika saya mulai menulis QueryProvider saya sendiri
TheSoftwareJedi

53

Membawa saya cara terlalu lama untuk menyadari bahwa banyak metode ekstensi LINQ seperti Single(), SingleOrDefault()dll memiliki overloads yang mengambil lambdas.

Anda dapat melakukan :

Single(x => x.id == id)

dan tidak perlu mengatakan ini - yang mana beberapa tutorial buruk membuat saya terbiasa melakukannya

Where(x => x.id == id).Single()

+1, sangat bagus. Saya akan mengingatnya.
Pretzel

4
Saya terus melupakan ini juga. Itu juga benar Count(), antara lain. Apakah Anda tahu jika ada perbedaan kinerja, selain bonus keterbacaan kode yang jelas?
Justin Morgan

1
Di universitas, dosen saya ingin mengambil poin karena menggunakan kelebihan ini !! Saya sudah membuktikan dia salah!
TDaver

12
Ini mungkin terdengar aneh tapi saya lebih suka sintaks kedua. Saya merasa lebih mudah dibaca.
Konamiman

40

Dalam LINQ to SQL saya selalu melihat orang-orang tidak memahami DataContext, bagaimana bisa digunakan dan bagaimana harus digunakan. Terlalu banyak orang tidak melihat DataContext untuk apa itu, objek Unit Kerja, bukan objek yang persisten.

Saya telah melihat banyak kali di mana orang mencoba untuk melajang sebuah DataContext / sesi itu / etc daripada membuat waktu baru untuk setiap operasi.

Dan kemudian ada pembuangan DataContext sebelum IQueryable telah dievaluasi tetapi itu lebih merupakan suatu kecenderungan ketika orang tidak memahami IQueryable daripada DataContext.

Konsep lain yang saya lihat banyak kebingungan adalah Query Syntax vs Expression Syntax. Saya akan menggunakan mana yang paling mudah pada saat itu, sering bertahan dengan Ekspresi Sintaks. Banyak orang masih tidak menyadari bahwa mereka akan menghasilkan hal yang sama pada akhirnya, Query dikompilasi menjadi Ekspresi.


2
Peringatan: Unit kerja dapat berupa program kecil dengan konteks data sebagai singleton.
graffic

15
Anda tidak boleh menggunakan DataContext dalam singleton, itu bukan thread yang aman.
Aaron Powell

3
@Slace, tidak semua program multitheaded, sehingga sangat OK untuk memiliki DataContext sebagai tunggal dalam banyak perangkat lunak "desktop"
Ian Ringrose

2
Saya digigit oleh ini (menggunakan DataContext sebagai singleton) ketika saya melakukan proyek LINQ pertama saya ke SQL. Saya tidak berpikir bahwa dokumentasi dan buku-buku membuatnya cukup jelas. Sebenarnya, saya pikir namanya bisa ditingkatkan, tapi saya tidak yakin bagaimana caranya.
Roger Lipscombe

1
Butuh membaca artivle ScottGu di Linq beberapa kali untuk mendapatkan ini ditumbuk di kepalaku.
Evan Plaice

34

Saya pikir yang disalahpahami bagian dari LINQ adalah bahwa itu adalah ekstensi bahasa , bukan perpanjangan database atau konstruk.

LINQjauh lebih dari LINQ to SQL.

Sekarang sebagian besar dari kita telah menggunakan LINQkoleksi, kita tidak akan pernah kembali!

LINQ adalah fitur paling signifikan untuk .NET sejak Generics in 2.0, dan Anonymous Types in 3.0.

Dan sekarang kita memiliki Lambda, saya tidak sabar untuk pemrograman paralel!


Saya bahkan menyebutnya lebih penting daripada jenis anonim, dan mungkin lebih dari sekadar obat generik.
Justin Morgan

26

Saya yakin ingin tahu apakah saya perlu tahu apa itu pohon ekspresi, dan mengapa.


6
Saya pikir perlu mengetahui apa itu pohon ekspresi dan mengapa pohon itu ada, tetapi bukan rincian tentang bagaimana membangunnya sendiri. (Mereka sulit dibangun dengan tangan, tetapi kompiler akan melakukan pekerjaan dengan baik ketika mengubah ekspresi lambda.)
Jon Skeet

3
Sebenarnya, saya berpikir untuk melakukan beberapa entri blog di pohon ekspresi (karena saya memang "mendapatkannya"). Saya menemukan memanipulasi pohon ekspresi sangat berguna ...
Marc Gravell

Namun, saya pikir mereka tidak membantu pembicaraan Jon ;-p
Marc Gravell

3
Saya hanya khawatir pohon ekspresi akan menjadi seperti pernyataan hasil: sesuatu yang ternyata sangat berharga meskipun pada kenyataannya saya tidak mengerti untuk apa itu pada awalnya.
Robert Rossney

1
Marc Gravell Saya ingin membaca entri blog Anda tentang masalah ini. Menantikan hal itu
Alexandre Brisebois

20

Saya cukup baru untuk LINQ. Inilah beberapa hal yang membuat saya tersandung dalam usaha pertama saya

  • Menggabungkan beberapa pertanyaan menjadi satu
  • Secara efektif men-debug query LINQ di Visual Studio.

21
Debugging LINQ adalah topik dengan sendirinya, dan yang penting. Saya pikir kelemahan terbesar dari LINQ adalah bahwa ia memungkinkan Anda menulis blok-blok logika rumit yang tidak dapat Anda lewati.
Robert Rossney

3
ini mungkin tempat yang baik untuk menggunakan pad LINQ
Maslow.

2
Setuju dengan sepenuh hati; itu sebabnya saya menulis Rahasia LINQ Terungkap: Chaining and Debugging , baru saja diterbitkan di Simple-Talk.com, yang mungkin Anda temukan bantuan.
Michael Sorens

Ya, LinqPad adalah alat sekunder yang bagus untuk mengembangkan kueri LINQ Anda. Terutama ketika memulai dan Anda baru mengenal konvensi / pola.
Buffalo

20

Sesuatu yang saya awalnya tidak sadari adalah bahwa sintaks LINQ tidak memerlukan IEnumerable<T>atau IQueryable<T>untuk bekerja, LINQ hanya tentang pencocokan pola.

alt teks http://bartdesmet.info/images_wlw/QIsIQueryabletheRightChoiceforMe_13478/image_thumb_3.png

Inilah jawabannya (tidak, saya tidak menulis blog itu, Bart De Smet melakukannya, dan dia salah satu blogger terbaik di LINQ yang saya temukan).


1
Anda mungkin menemukan posting blog ini juga menarik: msmvps.com/blogs/jon_skeet/archive/2008/02/29/…
Jon Skeet

Posting yang bagus Jon (saya berlangganan blog Anda, hanya baru-baru ini saja).
Aaron Powell

19

Saya masih memiliki masalah dengan perintah "let" (yang saya tidak pernah menemukan digunakan untuk) dan SelectMany (yang saya gunakan, tapi saya tidak yakin saya melakukannya dengan benar)


2
Setiap kali Anda ingin memperkenalkan variabel, Anda akan menggunakan pernyataan let. Pikirkan loop tradisional di mana Anda memperkenalkan variabel di dalamnya dan memberikan nama masing-masing variabel untuk membantu keterbacaan kode. Kadang-kadang juga bagus di mana Anda memiliki pernyataan membiarkan mengevaluasi hasil fungsi, yang kemudian Anda dapat memilih dan memesan pada tanpa harus mengevaluasi hasil dua kali.
Rob Packwood

'let' memungkinkan Anda untuk melakukan tipe komposit. Barang berguna.
Phill

19

Memahami kapan abstraksi di antara penyedia Linq bocor. Beberapa hal berfungsi pada objek tetapi tidak SQL (mis., TakeWhile). Beberapa metode dapat diterjemahkan ke dalam SQL (ToUpper) sementara yang lain tidak. Beberapa teknik lebih efisien dalam objek di mana yang lain lebih efektif dalam SQL (metode bergabung yang berbeda).


1
Ini adalah poin yang sangat bagus. Tidak membantu bahwa Intellisense akan menunjukkan kepada Anda SEMUA dari mereka dan biasanya akan dikompilasi. Kemudian Anda meledak saat runtime. Saya harap VS 2010 melakukan pekerjaan yang lebih baik untuk menunjukkan metode penyuluhan yang relevan.
Jason Short

12

Beberapa barang.

  1. Orang menganggap Linq sebagai Linq to SQL.
  2. Beberapa orang berpikir bahwa mereka dapat mulai mengganti semua foreach / logic dengan query Linq tanpa mempertimbangkan implikasi kinerja ini.

11

OK, karena permintaan, saya telah menulis beberapa hal Ekspresi. Saya tidak 100% senang dengan bagaimana blogger dan LiveWriter berkonspirasi untuk memformatnya, tetapi itu akan berlaku untuk saat ini ...

Ngomong-ngomong, begini ... Saya suka umpan balik, terutama jika ada area di mana orang ingin informasi lebih lanjut.

Ini dia , suka atau benci ...


10

Beberapa pesan kesalahan, terutama dari LINQ ke SQL bisa sangat membingungkan. menyeringai

Saya telah digigit oleh eksekusi yang ditunda beberapa kali seperti orang lain. Saya pikir hal yang paling membingungkan bagi saya adalah Penyedia Kueri SQL Server dan apa yang dapat dan tidak bisa Anda lakukan dengannya.

Saya masih kagum dengan kenyataan Anda tidak dapat melakukan Jumlah () pada kolom desimal / uang yang terkadang kosong. Menggunakan DefaultIfEmpty () tidak akan berfungsi. :(


1
Harus mudah prett menampar mana di mana pada permintaan itu untuk membuat jumlah bekerja
Esben Skov Pedersen

9

Saya pikir hal yang hebat untuk dibahas di LINQ adalah bagaimana Anda bisa mendapatkan diri Anda sendiri dalam masalah kinerja. Misalnya, menggunakan hitungan LINQ sebagai kondisi loop benar-benar tidak pintar.


7

IQueryable itu menerima keduanya, Expression<Func<T1, T2, T3, ...>>dan Func<T1, T2, T3, ...>, tanpa memberi petunjuk tentang penurunan kinerja pada kasus ke-2.

Berikut ini contoh kode, yang menunjukkan apa yang saya maksud:

[TestMethod]
public void QueryComplexityTest()
{
    var users = _dataContext.Users;

    Func<User, bool>                funcSelector =       q => q.UserName.StartsWith("Test");
    Expression<Func<User, bool>>    expressionSelector = q => q.UserName.StartsWith("Test");

    // Returns IEnumerable, and do filtering of data on client-side
    IQueryable<User> func = users.Where(funcSelector).AsQueryable();
    // Returns IQuerible and do filtering of data on server side
    // SELECT ... FROM [dbo].[User] AS [t0] WHERE [t0].[user_name] LIKE @p0
    IQueryable<User> exp = users.Where(expressionSelector);
}

Bisakah Anda jelaskan? Saya tidak mengikuti ...
Pretzel

@Pretzel Saya telah menambahkan contoh kode, yang menunjukkan masalah saya.
Valera Kolupaev

Terima kasih untuk contoh kodenya! Sangat membantu.
Buffalo

6

Saya tidak tahu apakah itu memenuhi syarat sebagai disalahpahami - tetapi bagi saya, tidak diketahui.

Saya senang belajar tentang DataLoadOptions dan bagaimana saya bisa mengontrol tabel mana yang bergabung ketika saya membuat kueri tertentu.

Lihat di sini untuk info lebih lanjut: MSDN: DataLoadOptions


6

Saya akan mengatakan aspek LINQ yang paling disalahpahami (atau harus tidak dipahami?) Adalah penyedia LINQ yang dapat IQueryable dan kustom .

Saya telah menggunakan LINQ untuk sementara waktu sekarang dan saya benar-benar nyaman di dunia IEnumerable, dan dapat menyelesaikan sebagian besar masalah dengan LINQ.

Tetapi ketika saya mulai melihat dan membaca tentang IQueryable, dan penyedia Ekspresi dan LINQ kustom itu membuat kepala saya berputar. Lihatlah bagaimana LINQ to SQL berfungsi jika Anda ingin melihat beberapa logika yang cukup rumit.

Saya berharap dapat memahami aspek LINQ ...


6

Seperti yang dikatakan kebanyakan orang, saya pikir bagian yang paling disalahpahami adalah mengasumsikan LINQ hanyalah pengganti T-SQL. Manajer saya yang menganggap dirinya sebagai guru TSQL tidak akan membiarkan kami menggunakan LINQ dalam proyek kami dan bahkan membenci MS karena merilis hal seperti itu !!!


Terlalu banyak orang menggunakannya sebagai pengganti TSQL. Sebagian besar dari mereka belum pernah mendengar rencana eksekusi.
erikkallen

+1 karena saya setuju dengan manajer Anda, setidaknya sejauh mengizinkan LINQ ke SQL dalam proyek apa pun. LINQ to Objects adalah masalah lain sepenuhnya.
NotMe

5

Apa yang diwakili var saat kueri dieksekusi?

Apakah iQueryable, iSingleResult, iMultipleResult, atau apakah itu berubah berdasarkan pada pelaksanaannya. Ada beberapa spekulasi tentang penggunaan (yang tampaknya) dinamis-mengetik vs standar statis mengetik di C #.


AFAIK var selalu merupakan kelas konkret yang dipermasalahkan (bahkan jika itu adalah tipe anonim) sehingga tidak pernah IQueryable, ISingleResult atau apa pun yang dimulai dengan 'I' (kelas konkret yang dimulai dengan 'I' tidak perlu diterapkan).
Motti

5

Betapa mudahnya membuat sarang adalah sesuatu yang saya pikir tidak semua orang mengerti.

Sebagai contoh:

from outerloopitem in outerloopitems
from innerloopitem in outerloopitem.childitems
select outerloopitem, innerloopitem

+1, whoa. itu cukup kuat.
Pretzel

4

group by masih membuat kepalaku berputar.

Setiap kebingungan tentang eksekusi yang ditangguhkan harus dapat diselesaikan dengan melangkah melalui beberapa kode sederhana berbasis LINQ dan bermain-main di jendela arloji.


1
Saya telah menemukan bahwa menerapkan sedikit LINQ ke Objek untuk bersenang-senang benar-benar membantu :) Tapi ya, ini agak membingungkan - tentu saja jika saya belum melakukan LINQ untuk sementara waktu saya harus kembali ke tanda tangan. Demikian juga "gabung" vs "gabung ke" sering membuatku ...
Jon Skeet

4

Kueri Terkompilasi

Kenyataan bahwa Anda tidak dapat rantai IQueryablekarena mereka metode panggilan (sementara masih tidak lain SQL translateable!) Dan itu hampir mustahil untuk bekerja di sekitar itu mindboggling dan menciptakan pelanggaran besar KERING. Saya membutuhkan IQueryableiklan ad-hoc di mana saya tidak mengkompilasi kueri (saya hanya mengkompilasi kueri untuk skenario berat), tetapi dalam kueri yang dikompilasi saya tidak dapat menggunakannya dan sebagai gantinya perlu menulis sintaks kueri reguler lagi. Sekarang saya melakukan subqueries yang sama di 2 tempat, perlu diingat untuk memperbarui keduanya jika ada perubahan, dan sebagainya. Mimpi buruk.


4

Saya pikir kesalahpahaman # 1 tentang LINQ ke SQL adalah bahwa Anda MASIH HARUS TAHU SQL untuk memanfaatkannya secara efektif.

Hal lain yang disalahpahami tentang Linq ke Sql adalah bahwa Anda masih harus menurunkan keamanan basis data hingga tidak masuk akal untuk membuatnya bekerja.

Poin ketiga adalah bahwa menggunakan Linq ke Sql bersama dengan kelas Dynamic (artinya definisi kelas dibuat saat runtime) menyebabkan sejumlah besar kompilasi just-in-time. Yang benar-benar dapat mematikan kinerja.


4
Sangat bermanfaat untuk mengetahui SQL. Beberapa SQL yang dipancarkan oleh Linq ke SQL (dan ORM lainnya) bisa sangat meragukan, dan mengetahui SQL membantu mendiagnosis masalah tersebut. Juga, Linq to SQL dapat menggunakan prosedur tersimpan.
Robert Harvey


2

Seperti disebutkan, lazy loading dan eksekusi ditangguhkan

Bagaimana LINQ ke Objek dan LINQ ke XML (IEnumerable) berbeda dari LINQ ke SQL (IQueryable)

BAGAIMANA membangun Lapisan Akses Data, Lapisan Bisnis, dan Lapisan Presentasi dengan LINQ di semua lapisan .... dan contoh yang bagus.


Dua yang pertama bisa saya lakukan. Aku tidak akan seperti untuk mencoba melakukan ketiga belum di "ini adalah cara yang tepat untuk melakukannya" akal ...
Jon Skeet

+1, sampai Anda menunjukkannya, saya tidak menyadari bahwa LINQ-to-Objects dan LINQ-to-XML adalah IEnumerable yang bertentangan dengan LINQ-to-SQL sebagai IQueryable, tetapi masuk akal. Terima kasih!
Pretzel

2

Seperti yang dikatakan kebanyakan orang, saya pikir bagian yang paling disalahpahami adalah mengasumsikan LINQ hanyalah pengganti T-SQL. Manajer saya yang menganggap dirinya sebagai guru TSQL tidak akan membiarkan kami menggunakan LINQ dalam proyek kami dan bahkan membenci MS karena merilis hal seperti itu !!!


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.