Интересни аспекти на работната LINQ към SQL

На предишния ми пост се проведе миналия месец, аз мисля, че е време да продължи. Този път, нека да поговорим за наследяване Mapping'e, добре, специален интерес в края на статията ще бъде изненада.

Проблеми с дискриминатор

Разбира се, ние поддържаме в нашата база данни полиморфен характер на данните. Например, тя е същността на CustomerOperation, което отразява една операция, която може да се извърши на потребителя. Операциите се извършват основно чрез услугите, така че има CustomerServiceOperation наследник, и тъй като ние имаме механизъм WebTracking'a, за които има WebTrackingOperation. Но достатъчно думи, това е по-добре да се покаже кода:

Нека се опитаме да получите някои WebTracking-операция. И тук е кода:

Всичко е наред. Но какво ще стане, ако изведнъж забравили да се регистрират тип WebTrackingOperation в Наследяването Картографиране атрибути? Ако забравите да направите това, такова искане ще се излъчва

Смарт LINQ към SQL! Но по изключение ще бъде най-добрият избор, по мое скромно мнение. Е, какво, ако сме забравили да се регистрират на вида, но ходи на масата на тези операции и някакъв вид променило директно в основата на сценария (например, преди всички операции бяха обслужване на клиенти, а сега има WebTracking, и някои от старите необходимостта да се актуализира). Нека се опитаме да извадим работа с непознати дискриминатор от базата данни:

Интересни аспекти на работната LINQ към SQL

Очаквано приспада същност база тип. Какво става, ако се опита да спаси такова чудо?

Нищо ужасно се е случило. Името се е променило, а останалата част остава един и същ:

Интересни аспекти на работната LINQ към SQL

Добре, нека се опитаме да го напиша в WebTrackingOperation основа (с рефакториране, което описах по-горе, кодът за създаване на такива субекти, непременно са се появили). Този опит ще се провали с смешно NullReference, доказателство:

Интересни аспекти на работната LINQ към SQL

Макар и да не се обърне внимание, че грешката се влюбва в мистериозната Mindbox.Data.Linq.dll, той попада по същия начин, както в класическия LINQ към SQL. От грешки, както можете да видите, като цяло никога не съм виждал, къде да търсим проблема, така че грешката не е много приятно. Бъдете внимателни и не забравяйте да посочите всички видове полиморфна, предприятието отнася Наследяването Mapping'a.

И накрая, за забавна дискриминатор собственост. Нека се опитаме да създадем един нов субект базов клас CustomerOperation, присвоява дискриминатор и да спасят:

Така ще се въведат стойност, генерирана поле Discriminator = WebTracking, но след вложка perevystavit самата дискриминатор LINQ към SQL - това е, защото тя да сетер с празен низ (защото това е стойността по подразбиране за базовия тип е посочен в Картографиране атрибут за наследството):

Интересни аспекти на работната LINQ към SQL

Ако това поведение не е за вас, а след това има един прост трик: в setter'e дискриминатор да се игнорира експозицията на празен низ.

неканена изброяване

В LINQ към SQL, има една (не, добре, разбира се никога не сам, но сега това е за конкретна) много неприятно момент. Почти винаги, когато се подаде заявление за LINQ е построен по такъв начин, че не е възможно smappit на SQL, LINQ, когато се обаждате на Enumerator хвърля изключение. Текстът на тези изключения е известно на всички, че може да бъде нещо като (отне няколко bugtracker): "достъп държава" System.DateTime DateTimeUtc "на" Itc.DirectCrm.Model.CustomerAction "не правен от типа" System.Linq.IQueryable` 1 [Itc.DirectCrm.Model.CustomerAction] "или" метод "булева Оценка [CustomerLotteryTicket, булева] (System.Linq.Expressions.Expression`1 [System.Func`2 [Itc.DirectCrm.Promo.CustomerLotteryTicket, System.Boolean ]], Itc.DirectCrm.Promo.CustomerLotteryTicket) "не се поддържа превод на SQL". Ключовата дума тук е почти. Понякога LINQ към SQL може да откриете, че вместо да се хвърлят такова изключение, по-добре е да удържи повече субекти в паметта и памет трябва да се направят някои промени. Това е много тъжно, поради няколко причини: това не е документирано по някакъв начин (доколкото ми е известно), тъй като на този, понякога попадат на OutOfMemory (тъй като се възприема същността никога няма да напусне контекста, въпреки че кодът ще изглежда, като че ли се приспадат анонимни предмети, които бързо ще се съберат GC), както и заради грешки с Наследяването картографиране. Всъщност, нека да разгледаме бъг.

действие шаблон с идентификатор = 20 е EmailMailingActionTemplate - електронната поща за кореспонденция (проверих), нека си го изважда:

Интересни аспекти на работната LINQ към SQL

Добре. Сега се опитайте да се поиска тази дейност да се изпълни искането на шаблона zabagovanny:

Id Нарочно ограничен, за да се покаже, че това е заявка за проблем. EffectiveEndDateTimeUtc не е колона, това е просто едно свойство на класа, в предната част на избор не се нарича AsEnumerable предизвикателство, така че на теория LINQ ще трябва да хвърли една от самите изключения, примери за което съм ти, цитирани по-горе. Но не. Заявка е преведен на следния SQL (просто да поиска лице):

След като предварителна заявка (извади конкретен шаблон Id) връща модела действие на база вида:

Автоматично изброяване, както се вижда от този резултат, просто не обръщат внимание на Наследяването картографиране, винаги създава същността на базовия клас. Автоматично изброяване е страх!

Боя се, че това не е точно отразяват какво всъщност се случва. LINQ не третира следния код като AsEnumerable. Извършва картографиране, така че mapitsya, а останалата част се извършва в паметта. Например, ако го направите, ще бъдат създадени не само Изберете предприятието свойства и качества Изберете от свързано лице в паметта само свързани лица.
Добър съвет би бил да не притежава свойствата лице, което не се mapyatsya на SQL - което ги прави методите, които веднага се виждаше проблем.

FixedItems

Може би сте забелязали, че частта, където се говори за проблемите с дискриминатор, аз се въведе следния код:

Както можете да се досетите, всякакъв достъп до базата данни на кода чрез хранилището. Всеки хранилище има помощник методи за работа в предприятието за някои функции (потребители метод хранилище GetByName и т.н.), но, тъй като тя не е практично за всеки да създаде свой собствен метод, а след това всеки един от нашите хранилище е собственост на артикули - това е просто IQueryable съответните лица. Но това, което е FixedItems на CustomerOperationRepository? Смешното е - FixedItems имот в кода изглежда така:

Това е - един от патериците, че ние трябва да използват при работа с LINQ към SQL. За да се обясни на проблема, е необходимо да се опише ситуацията малко.

CustomerOperationRepository дава достъп до лица CustomerOperation, но самата същност на CustomerOperation е елемент от кампанията (голямата ни единица, към която е свързана с много други обекти). Тези лица като потвърждение, че има много общи свойства, така че те са наследени от един клас хранилище и те също са наследени. Както всички елементи на кампанията са наследени от ICampaignItem интерфейс, както и базов клас хранилище получава тип обект като първи параметър generic'a:

Проблемът е, че методите на този клас на лечението до полетата TCampaignItem извършва с помощта ICampaignItem интерфейс и няма да имат известен проблем. че LINQ към SQL не MAPPA свойства, определени в интерфейсите. Затова поиска, например, всички операции, свързани с кампанията (Където (т => item.CampaignId == campaign.Id)), намалява с InvalidOperationException: MappingOfInterfacesMemberIsNotSupported: ICampaignItem, CAMPAIGNID. В същото време като към IQueryable верига на пръв поглед безполезен Select (т => т) магически разреши всички проблеми. Същият метод може да се използва вместо object.Equals оператор ==: Когато (т => равно (item.CampaignId, campaign.Id)), това искане се предава без използването на FixedItems.

Null / Не е нула в различни наследници

Кратко и просто грешка. LINQ към SQL не поддържа една и съща област на различни настройки за нищожна и не нула в наследниците. Например:

PerformActionCustomerServiceOperation изисква стойност operationStepGroupId е винаги (това изисква модел на домейн), но едно и също поле в operationStepGroupId IdentificationTrackingOperation може да липсват. Основният клас за две лица - CustomerOperation, в които не operationStepGroupId не, се прибавя тази област отделно в PerformActionCustomerServiceOperation и IdentificationTrackingOperation.

Ето как се случва. Обърнете внимание на ценности CanBeNull и вида на високоговорители в различни лица:

Е, добре, но сега ще се опитаме да се чете от базата данни в списъка на операциите. Един от тези операции - IdentificationTrackingOperation, което липсва OperationStepGroupId.

Опитът се провали, както се очаква, тъй като ние се InvalidOperationException: CannotAssignNull: System.Int32.

Как да се справим с него? Ние направихме лесно - LINQ позволено да PerformActionCustomerServiceOperation няма стойности за OperationStepGroupId и неговото утвърждаване е отделна проверка.

Тези, които четат, изненада

Може би сте забелязали, че грешките в LINQ в моите снимки на екрани са включени в групата, наречена Mindbox.Data.Linq.dll. Да, ние forknuli LINQ към SQL. Например, сега за регистрация на лица, наследниците могат да се използват не само InheritanceMappingAttribute, но AddInheritance на метод нищожно(Код на обекта) на Mindbox.Data.Linq.Mapping.MindboxMappingConfiguration, което позволява на наследниците да се регистрират лица в различни възли.

Нашата вилица може да се постави чрез Nuget: Mindbox.Data.Linq.

Може би искате нещо, което да помогне или да се възползват от - на добър час с това.