Что такое хорошие методы дизайна при работе с Платформой Объекта

Это будет применяться главным образом для приложения asp.net, где к данным не получают доступ через soa. Подразумевать, что Вы получаете доступ к объектам, загруженным из платформы, не Объектов Передачи, хотя некоторая рекомендация все еще применяются.

Это - общественное сообщение, поэтому добавьте к нему, как Вы считаете целесообразным.

Относится: Платформа Объекта 1,0 поставленных с Visual Studio 2008 sp1.

Почему выбор EF во-первых?

Рассмотрение его является молодой технологией с большим количеством проблем (см. ниже), это может быть навязывание товара для преуспевания в побеждающую сторону EF для проекта. Однако это - технология, которую продвигает Microsoft (за счет Linq2Sql, который является подмножеством EF). Кроме того, Вы не можете быть удовлетворены NHibernate или другими решениями там. Безотносительно причин существуют люди там (включая меня) работающий с EF, и жизнь не является bad.make, Вы думаете.

EF и наследование

Первым большим предметом является наследование. EF действительно поддерживает отображение для наследованных классов, которые сохраняются 2 способами: таблица в классе и таблица иерархия. Моделирование легко и нет никаких проблем программирования с той частью.

(Следующее относится к таблице на модель класса, поскольку у меня нет опыта с таблицей на иерархию, которая, так или иначе, ограничена.) Настоящая проблема возникает, когда Вы пробуете к выполнению запросов, которое включает один или несколько объектов, которые являются частью дерева наследования: сгенерированный sql невероятно ужасен, занимает много времени, чтобы быть проанализированным EF и занимает много времени для выполнения также. Это - реальный выставочный стопор. Достаточно это EF не должно, вероятно, использоваться с наследованием или как можно меньше.

Вот пример того, как плохо это было. Модель My EF имела ~30 классов, ~10 из которых были частью дерева наследования. При выполнении запроса для получения одного объекта от Базового класса, что-то столь же простое как Основа. Доберитесь (идентификатор), сгенерированный SQL был более чем 50 000 символов. Тогда, когда Вы пытаетесь возвратить некоторые Ассоциации, это ухудшается еще больше, идя до выдачи исключений SQL о неспособности запросить больше чем 256 таблиц сразу.

Хорошо, это плохо, понятие EF состоит в том, чтобы позволить Вам создавать свою структуру объекта без (или с как можно меньше) соображение на фактической реализации базы данных Вашей таблицы. Это полностью перестало работать в этом.

Так, рекомендации? Избегайте наследования, если Вы можете, производительность быть настолько лучше. Используйте его экономно, где Вы имеете к. По-моему, это делает EF прославленным инструментом sql-поколения для запросов, но существуют все еще преимущества для использования его. И способы реализовать механизм, которые подобны наследованию.

Обход наследования с Интерфейсами

Первая вещь знать с попыткой получить некоторое наследование, идущее с EF, состоит в том, что Вы не можете присвоить non-EF-modeled классу базовый класс. Даже не пробуйте его, это будет перезаписано разработчиком моделей. Таким образом, что сделать?

Можно использовать интерфейсы для осуществления того, классы реализуют некоторую функциональность. Например, вот интерфейс IEntity, которые позволяют Вам определять Ассоциации между объектами EF, где Вы не знаете во время проектирования, каков тип объекта был бы.

public enum EntityTypes{ Unknown = -1, Dog = 0, Cat }
public interface IEntity
{
    int EntityID { get; }
    string Name { get; }
    Type EntityType { get; }
}
public partial class Dog : IEntity
{
   // implement EntityID and Name which could actually be fields 
   // from your EF model
   Type EntityType{ get{ return EntityTypes.Dog; } }
}

Используя этот IEntity, можно тогда работать с неопределенными ассоциациями в других классах

// lets take a class that you defined in your model.
// that class has a mapping to the columns: PetID, PetType
public partial class Person
{
    public IEntity GetPet()
    {
        return IEntityController.Get(PetID,PetType);
    }
}

который использует некоторые дополнительные функции:

public class IEntityController
{
    static public IEntity Get(int id, EntityTypes type)
    {
        switch (type)
        {
            case EntityTypes.Dog: return Dog.Get(id);
            case EntityTypes.Cat: return Cat.Get(id);
            default: throw new Exception("Invalid EntityType");
        }
    }
}

Не столь аккуратный как наличие простого наследования, особенно рассмотрение необходимо сохранить PetType в дополнительном поле базы данных, но рассмотрение увеличения производительности, я не оглянулся бы назад.

Это также не может смоделировать one-many, many-many отношения, но с творческим использованием 'Объединения' это могло быть сделано работать. Наконец, это создает сторону, слабую из загружающихся данных в свойстве/функции объекта, относительно которого необходимо быть осторожны. Используя четкое соглашение о присвоении имен как GetXYZ () помогает в этом отношениям.

Скомпилированные запросы

Производительность Платформы объекта не так хороша как прямой доступ к базе данных с ADO (очевидно), или Linq2SQL. Существуют способы улучшить его однако, один из которых компилирует Ваши запросы. Производительность скомпилированного запроса подобна Linq2Sql.

Что такое скомпилированный запрос? Это - просто запрос, для которого Вы говорите платформе сохранять проанализированное дерево в памяти, таким образом, это не должно быть повторно создано в следующий раз, когда Вы выполняете его. Так следующий запуск, Вы сэкономите время, которое требуется для парсинга дерева. Не обесценивайте это, поскольку это - очень дорогостоящая операция, которая становится еще хуже с более сложными запросами.

Существует 2 способа скомпилировать запрос: создание ObjectQuery с EntitySQL и использованием CompiledQuery. Скомпилируйте () функцию. (Обратите внимание при помощи EntityDataSource на Вашей странице, на самом деле использование ObjectQuery с EntitySQL так, чтобы был скомпилирован и кэшировался).

В стороне здесь в случае, если Вы не знаете, каков EntitySQL. Это - основанный на операция со строками способ записать запросы против EF. Вот пример: "выберите собаку значения из Объектов. DogSet как собака, где собака. ИДЕНТИФИКАТОР = @ID". Синтаксис довольно подобен синтаксису SQL. Можно также сделать управление довольно сложным объектом, которое хорошо объяснено [здесь] [1].

Хорошо, таким образом, вот то, как сделать это использование ObjectQuery <>

        string query = "select value dog " +
                       "from Entities.DogSet as dog " +
                       "where dog.ID = @ID";

        ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>(query, EntityContext.Instance));
        oQuery.Parameters.Add(new ObjectParameter("ID", id));
        oQuery.EnablePlanCaching = true;
        return oQuery.FirstOrDefault();

В первый раз, когда Вы выполняете этот запрос, платформа генерирует дерево выражений и сохранит его в памяти. Так в следующий раз, когда это выполняется, Вы будете экономить на том дорогостоящем шаге. В том примере EnablePlanCaching = верный, который является ненужным, так как это - опция по умолчанию.

Другим способом скомпилировать запрос для более позднего использования является CompiledQuery. Скомпилируйте метод. Это использует делегата:

    static readonly Func<Entities, int, Dog> query_GetDog =
        CompiledQuery.Compile<Entities, int, Dog>((ctx, id) =>
            ctx.DogSet.FirstOrDefault(it => it.ID == id));

или использование linq

    static readonly Func<Entities, int, Dog> query_GetDog =
        CompiledQuery.Compile<Entities, int, Dog>((ctx, id) =>
            (from dog in ctx.DogSet where dog.ID == id select dog).FirstOrDefault());

назвать запрос:

query_GetDog.Invoke( YourContext, id );

Преимущество CompiledQuery состоит в том, что синтаксис Вашего запроса проверяется во время компиляции, где, поскольку EntitySQL не. Однако существует другое соображение...

Включает

Позволяет говорят, что Вы хотите иметь данные для владельца собаки, чтобы быть возвращенными запросом, чтобы не выполнять 2 вызова к базе данных. Легкий сделать, исправьтесь?

EntitySQL

        string query = "select value dog " +
                       "from Entities.DogSet as dog " +
                       "where dog.ID = @ID";
        ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>(query, EntityContext.Instance)).Include("Owner");
        oQuery.Parameters.Add(new ObjectParameter("ID", id));
        oQuery.EnablePlanCaching = true;
        return oQuery.FirstOrDefault();

CompiledQuery

    static readonly Func<Entities, int, Dog> query_GetDog =
        CompiledQuery.Compile<Entities, int, Dog>((ctx, id) =>
            (from dog in ctx.DogSet.Include("Owner") where dog.ID == id select dog).FirstOrDefault());

Теперь, что, если Вы хотите параметризовать Включение? То, что я имею в виду, - то, что Вы хотите иметь сингл, Получают () функцию, которая вызвана от различных страниц, которые заботятся о различных отношениях о собаке. Каждый заботится о Владельце, другом о его FavoriteFood, другом о его FavotireToy и так далее. Basicly, Вы хотите сказать запрос который ассоциации загрузиться.

Легко сделать с EntitySQL

public Dog Get(int id, string include)
{
        string query = "select value dog " +
                       "from Entities.DogSet as dog " +
                       "where dog.ID = @ID";

        ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>(query, EntityContext.Instance))
    .IncludeMany(include);
        oQuery.Parameters.Add(new ObjectParameter("ID", id));
        oQuery.EnablePlanCaching = true;
        return oQuery.FirstOrDefault();
}

Включение просто использует переданную строку. Достаточно легкий. Обратите внимание, что возможно изменить к лучшему Включать (строка) функция (который принимает только единственный путь) с IncludeMany (строка), которая позволит Вам передать строку разделенных от запятой ассоциаций для загрузки. Посмотрите далее в дополнительном разделе для этой функции.

Если мы пытаемся сделать это с CompiledQuery однако, мы сталкиваемся с многочисленными проблемами:

Очевидное

    static readonly Func<Entities, int, string, Dog> query_GetDog =
        CompiledQuery.Compile<Entities, int, string, Dog>((ctx, id, include) =>
            (from dog in ctx.DogSet.Include(include) where dog.ID == id select dog).FirstOrDefault());

будет дросселировать при вызове:

query_GetDog.Invoke( YourContext, id, "Owner,FavoriteFood" );

Поскольку, как упомянуто выше, Включают (), только хочет видеть единственный путь в строке, и здесь мы даем ее 2: "Владелец" и "FavoriteFood" (который не должен быть перепутан с "Владельцем. FavoriteFood"!).

Затем давайте использовать IncludeMany (), который является дополнительной функцией

    static readonly Func<Entities, int, string, Dog> query_GetDog =
        CompiledQuery.Compile<Entities, int, string, Dog>((ctx, id, include) =>
            (from dog in ctx.DogSet.IncludeMany(include) where dog.ID == id select dog).FirstOrDefault());

Неправильно снова на этот раз это - потому что EF не может проанализировать IncludeMany, потому что это не часть функций, которая является, распознает: это - расширение.

Хорошо, таким образом, Вы хотите передать произвольное число путей к Вашей функции, и Включает (), только берет единственный. Что сделать? Вы могли решить, что никогда не будете нуждаться в больше, чем, говорить 20, Включает, и передайте, каждый разделил строки в структуре к CompiledQuery. Но теперь запрос похож на это:

from dog in ctx.DogSet.Include(include1).Include(include2).Include(include3)
.Include(include4).Include(include5).Include(include6)
.[...].Include(include19).Include(include20) where dog.ID == id select dog

который ужасен также. Хорошо, тогда, но ожидают минута. Разве мы не можем возвратить ObjectQuery <> с CompiledQuery? Тогда установите включение в это? Ну, это, что я думал бы так также:

    static readonly Func<Entities, int, ObjectQuery<Dog>> query_GetDog =
        CompiledQuery.Compile<Entities, int, string, ObjectQuery<Dog>>((ctx, id) =>
            (ObjectQuery<Dog>)(from dog in ctx.DogSet where dog.ID == id select dog));
public Dog GetDog( int id, string include )
{
    ObjectQuery<Dog> oQuery = query_GetDog(id);
    oQuery = oQuery.IncludeMany(include);
    return oQuery.FirstOrDefault;   
}

Это должно было работать, за исключением того, что, когда Вы называете IncludeMany (или Включаете, Где, OrderBy...) Вы делаете недействительным кэшируемый скомпилированный запрос, потому что это - совершенно новое теперь! Так, дерево выражений должно быть повторно проанализировано, и Вы поражали ту производительность снова.

Таким образом, каково решение? Вы просто не можете использовать CompiledQueries с параметрическим, Включает. Используйте EntitySQL вместо этого. Это не означает, что нет использования для CompiledQueries. Это является большим для локализованных запросов, которые будут всегда называть в том же контексте. Идеально CompiledQuery должен всегда использоваться, потому что синтаксис проверяется во время компиляции, но из-за ограничения, это не возможно.

Пример использования был бы: можно хотеть иметь страницу, которая запрашивает, который у двух собак есть та же любимая еда, которая является немного узкой для функции BusinessLayer, таким образом, Вы помещаете ее в свою страницу и знаете точно, какой включает, требуются.

Передача больше чем 3 параметров к CompiledQuery

Func ограничен 5 параметрами, из которых последний является типом возврата, и первый является Вашим объектом Объектов из модели. Таким образом, это оставляет Вас с 3 параметрами. Гроши, но это может быть изменено к лучшему очень легко.

public struct MyParams
{
    public string param1;
    public int param2;
    public DateTime param3;
}

    static readonly Func<Entities, MyParams, IEnumerable<Dog>> query_GetDog =
        CompiledQuery.Compile<Entities, MyParams, IEnumerable<Dog>>((ctx, myParams) =>
            from dog in ctx.DogSet where dog.Age == myParams.param2 && dog.Name == myParams.param1 and dog.BirthDate > myParams.param3 select dog);

public List<Dog> GetSomeDogs( int age, string Name, DateTime birthDate )
{
    MyParams myParams = new MyParams();
    myParams.param1 = name;
    myParams.param2 = age;
    myParams.param3 = birthDate;
    return query_GetDog(YourContext,myParams).ToList();
}

Возвратите Типы (это не относится к запросам EntitySQL, поскольку они не компилируются одновременно во время выполнения как метод CompiledQuery),

Работая с Linq, Вы обычно не вызываете выполнение запроса до самого последнего момента, в случае, если некоторые другие функции в нисходящем направлении хотят изменить запрос в некотором роде:

    static readonly Func<Entities, int, string, IEnumerable<Dog>> query_GetDog =
        CompiledQuery.Compile<Entities, int, string, IEnumerable<Dog>>((ctx, age, name) =>
            from dog in ctx.DogSet where dog.Age == age && dog.Name == name select dog);

public IEnumerable<Dog> GetSomeDogs( int age, string name )
{
    return query_GetDog(YourContext,age,name);
}
public void DataBindStuff()
{
    IEnumerable<Dog> dogs = GetSomeDogs(4,"Bud");
    // but I want the dogs ordered by BirthDate
    gridView.DataSource = dogs.OrderBy( it => it.BirthDate );

}

Что собирается произойти здесь? Путем тихой игры с исходным ObjectQuery (который является фактическим типом возврата оператора Linq, который реализует IEnumerable), он будет делать недействительным скомпилированный запрос и будет силой, чтобы повторно проанализировать. Так, эмпирическое правило состоит в том, чтобы возвратить Список <> объектов вместо этого.

    static readonly Func<Entities, int, string, IEnumerable<Dog>> query_GetDog =
        CompiledQuery.Compile<Entities, int, string, IEnumerable<Dog>>((ctx, age, name) =>
            from dog in ctx.DogSet where dog.Age == age && dog.Name == name select dog);

public List<Dog> GetSomeDogs( int age, string name )
{
    return query_GetDog(YourContext,age,name).ToList(); //<== change here
}
public void DataBindStuff()
{
    List<Dog> dogs = GetSomeDogs(4,"Bud");
    // but I want the dogs ordered by BirthDate
    gridView.DataSource = dogs.OrderBy( it => it.BirthDate );

}

При вызове ToList (), запрос выполняется согласно скомпилированному запросу и затем, позже, OrderBy выполняется против объектов в памяти. Это может быть немного медленнее, но я даже не уверен. Один решенный вопрос состоит в том, что у Вас нет беспокойства о плохом обращении с ObjectQuery и лишении законной силы скомпилированного плана запросов.

Еще раз это не общий оператор. ToList () является приемом безопасного программирования, но если у Вас есть допустимая причина не использовать ToList (), идти вперед. Существует много случаев, в которых Вы хотели бы уточнить запрос прежде, чем выполнить его.

Производительность

Каково влияние производительности компиляции запроса? Это может на самом деле быть довольно большим. Эмпирическое правило - то, что компиляция и кэширование запроса для повторного использования занимают, по крайней мере, дважды время простого выполнения его без кэширования. Для сложных запросов (читает inherirante), я видел вверх к 10 секундам.

Так, в первый раз, когда предварительно скомпилированный запрос называют, Вы поражали производительность. После того первого хита производительность заметно лучше, чем тот же запрос non-compiled. Практически то же как Linq2Sql

При загрузке страницы предварительно скомпилированными запросами в первый раз, когда Вы получите хит. Это загрузится через, возможно, 5-15 секунд (очевидно, больше чем один предварительно скомпилированный запрос закончит тем, что был назван), в то время как последующие загрузки возьмут меньше чем 300 мс. Резкое различие, и Вам решать решить, нормально ли для Вашего первого пользователя, который получит удар или Вы хотите, чтобы сценарий назвал Ваши страницы для принуждения компиляции запросов.

Это может запросить кэшироваться?

{
    Dog dog = from dog in YourContext.DogSet where dog.ID == id select dog;
}

Нет, специальные запросы Linq не кэшируются, и Вы понесете расходы генерации дерева каждый раз, когда Вы называете его.

Параметрические запросы

Большинство возможностей поиска включает в большой степени параметризованные запросы. Существуют даже библиотеки, доступные, который позволит Вам создать параметрический запрос из lamba выражений. Проблема состоит в том, что Вы не можете использовать предварительно скомпилированные запросы с теми. Один путь вокруг этого состоит в том, чтобы планировать все возможные критерии в запросе и флаге, какой Вы хотите использовать:

public struct MyParams
{
    public string name;
public bool checkName;
    public int age;
public bool checkAge;
}

    static readonly Func<Entities, MyParams, IEnumerable<Dog>> query_GetDog =
        CompiledQuery.Compile<Entities, MyParams, IEnumerable<Dog>>((ctx, myParams) =>
            from dog in ctx.DogSet 
    where (myParams.checkAge == true && dog.Age == myParams.age) 
        && (myParams.checkName == true && dog.Name == myParams.name ) 
    select dog);

protected List<Dog> GetSomeDogs()
{
    MyParams myParams = new MyParams();
    myParams.name = "Bud";
    myParams.checkName = true;
    myParams.age = 0;
    myParams.checkAge = false;
    return query_GetDog(YourContext,myParams).ToList();
}

Преимущество здесь состоит в том, что Вы извлекаете всю пользу предварительно скомпилированного quert. Недостатки - то, что Вы, скорее всего, закончите с, где пункт, который довольно трудно поддержать, что Вы подвергнетесь большему штрафу за предварительную компиляцию запроса и что каждый запрос Вы работаете, не так эффективен, как это могло быть (особенно с добавленными соединениями).

Иначе должен создать часть запроса EntitySQL частью, как все мы сделали с SQL.

protected List<Dod> GetSomeDogs( string name, int age)
{
string query = "select value dog from Entities.DogSet where 1 = 1 ";
    if( !String.IsNullOrEmpty(name) )
        query = query + " and dog.Name == @Name ";
if( age > 0 )
    query = query + " and dog.Age == @Age ";

    ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>( query, YourContext );
    if( !String.IsNullOrEmpty(name) )
        oQuery.Parameters.Add( new ObjectParameter( "Name", name ) );
if( age > 0 )
        oQuery.Parameters.Add( new ObjectParameter( "Age", age ) );

return oQuery.ToList();
}

Здесь проблемы: - нет никакой проверки синтаксиса во время компиляции - каждая различная комбинация параметров генерирует различный запрос, который должен будет быть предварительно скомпилирован, когда это будет первый показ. В этом случае существует только 4 различных возможных запроса (никакие параметрические усилители, только для возраста, только для имени и оба параметрических усилителя), но Вы видите, что может быть путь больше с нормальным мировым поиском. - Никому не нравится связывать строки!

Другая опция состоит в том, чтобы запросить большое подмножество данных и затем сузить их в памяти. Это особенно полезно, если Вы работаете с определенным подмножеством данных, как все собаки в городе. Вы знаете, что существует много, но Вы также знаете, что нет то, что многие... так Ваша страница результатов поиска CityDog могут загрузить всех собак для города в памяти, которая является единственным предварительно скомпилированным запросом, и затем уточните результаты

protected List<Dod> GetSomeDogs( string name, int age, string city)
{
string query = "select value dog from Entities.DogSet where dog.Owner.Address.City == @City ";
    ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>( query, YourContext );
    oQuery.Parameters.Add( new ObjectParameter( "City", city ) );

List<Dog> dogs = oQuery.ToList();

if( !String.IsNullOrEmpty(name) )
        dogs = dogs.Where( it => it.Name == name );
if( age > 0 )
        dogs = dogs.Where( it => it.Age == age );

return dogs;
}

Особенно полезно, когда Вы начинаете отображаться, все данные тогда допускают фильтрацию.

Проблемы: - мог привести к серьезной передаче данных, если Вы не осторожны относительно своего подмножества. - Можно только отфильтровать на данных, которые Вы возвратили. Это означает это, если Вы не возвращаете Собаку. Ассоциация владельцев, Вы не будете в состоянии отфильтровать на Собаке. Владелец. Имя Поэтому, каково лучшее решение? Нет никого. Необходимо выбрать решение, которое работает лучше всего на Вас и Вашу проблему: - используют основанное на лямбде здание запроса, когда Вы не заботитесь о предварительной компиляции Ваших запросов. - Используют полностью определенный, предварительно скомпилировал запрос Linq, когда Ваша структура объекта не слишком сложна. - Используют конкатенацию EntitySQL/string, когда структура могла быть сложной и когда возможное количество различных получающихся запросов является небольшим (что означает меньше хитов перед компиляцией). - Используют фильтрацию в оперативной памяти, когда Вы работаете с небольшим подмножеством данных или когда необходимо было выбрать все данные по данным сначала так или иначе (если производительность согласится со всеми данными, то, просачиваясь память не заставит времени быть проведенным в дб).

Доступ Singleton

Лучший способ иметь дело с Вашим контекстом и объектами через все Ваши страницы состоит в том, чтобы использовать шаблон "одиночка":

public sealed class YourContext
{
    private const string instanceKey = "On3GoModelKey";

    YourContext(){}

    public static YourEntities Instance
    {
        get
        {
            HttpContext context = HttpContext.Current;
            if( context == null )
                return Nested.instance;

            if (context.Items[instanceKey] == null)
            {
                On3GoEntities entity = new On3GoEntities();
                context.Items[instanceKey] = entity;
            }
            return (YourEntities)context.Items[instanceKey];
        }
    }

    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly YourEntities instance = new YourEntities();
    }
}

NoTracking, действительно ли это стоит того?

При выполнении запроса можно сказать платформе отслеживать объекты, которые она возвратит или нет. Что это означает? С включенным отслеживанием (опция по умолчанию), платформа отследит то, что продолжает объект (это было изменено? Созданный? Удаленный?) и также свяжет объекты вместе, когда дальнейшие запросы сделаны из базы данных, которая является тем, что представляет интерес здесь.

Например, позволяет, предполагают, что у Собаки с идентификатором == 2 есть владелец который идентификатор == 10.

Dog dog = (from dog in YourContext.DogSet where dog.ID == 2 select dog).FirstOrDefault();
    //dog.OwnerReference.IsLoaded == false;
    Person owner = (from o in YourContext.PersonSet where o.ID == 10 select dog).FirstOrDefault();
    //dog.OwnerReference.IsLoaded == true;

Если бы мы должны были сделать то же без отслеживания, то результат отличался бы.

ObjectQuery<Dog> oDogQuery = (ObjectQuery<Dog>)
    (from dog in YourContext.DogSet where dog.ID == 2 select dog);
oDogQuery.MergeOption = MergeOption.NoTracking;
Dog dog = oDogQuery.FirstOrDefault();
    //dog.OwnerReference.IsLoaded == false;
ObjectQuery<Person> oPersonQuery = (ObjectQuery<Person>)
    (from o in YourContext.PersonSet where o.ID == 10 select o);
oPersonQuery.MergeOption = MergeOption.NoTracking;
    Owner owner = oPersonQuery.FirstOrDefault();
    //dog.OwnerReference.IsLoaded == false;

Отслеживание очень полезно и в идеальном мире без проблемы производительности, оно всегда шло бы. Но в этом мире, существует цена за него, с точки зрения производительности. Так, необходимо ли использовать NoTracking для ускорения вещей? Это зависит от того, для чего Вы планируете использовать данные.

Есть ли шанс, что данные, которые Ваш запрос с NoTracking может использоваться для создания, обновляют/вставляют/удаляют в базе данных? Если так, не используйте NoTracking, потому что ассоциации не прослежены, и будут исключения причин, которые будут брошены.

На странице, где нет absolutly никаких обновлений базы данных, можно использовать NoTracking.

Смешивание отслеживания и NoTracking возможно, но это требует, чтобы Вы, чтобы быть дополнительны осторожный с обновили/вставили/удалили. Проблема состоит в том, что, если Вы смешиваетесь тогда, Вы рискуете иметь платформу, пытающуюся Присоединить () объект NoTracking к контексту, где другая копия того же объекта существует с отслеживанием на. Basicly, что я говорю, является этим

Dog dog1 = (from dog in YourContext.DogSet where dog.ID == 2).FirstOrDefault();

ObjectQuery<Dog> oDogQuery = (ObjectQuery<Dog>)
    (from dog in YourContext.DogSet where dog.ID == 2 select dog);
oDogQuery.MergeOption = MergeOption.NoTracking;
Dog dog2 = oDogQuery.FirstOrDefault();

dog1 и dog2 являются 2 различными объектами, одним прослеженным и одним нет. Используя отделенный объект в обновлении/вставлении вызовет Присоединение (), который скажет, "Ожидают минута, у меня действительно уже есть объект здесь с тем же ключом базы данных. Сбой". И когда Вы Присоединяете () один объект, вся его иерархия присоединяется также, вызывая проблемы везде. Будьте дополнительны осторожный.

Сколько быстрее это с NoTracking

Это зависит от запросов. Некоторые - намного больше succeptible к отслеживанию, чем другой. У меня нет быстрого легким правилом для него, но это помогает.

Таким образом, я должен использовать NoTracking везде тогда?

Не точно. Существуют некоторые преимущества для отслеживания объекта. Первый - то, что объект кэшируется, таким образом, последующий вызов для того объекта не поразит базу данных. Тот кэш только допустим в течение времени жизни объекта YourEntities, который, если Вы используете одноэлементный код выше, совпадает со временем жизни страницы. Запрос на одну страницу == один объект YourEntity. Таким образом для множественных вызовов того же объекта, это загрузится только однажды на запрос страницы. (Другой механизм кэширования мог расширить это).

Что происходит, когда Вы используете NoTracking и попытку загрузить тот же объект многократно? База данных будет запрошена каждый раз, таким образом, будет влияние там. Как часто/должны, Вы призываете к тому же объекту во время единственного запроса страницы? Как можно меньше, конечно, но это делает происходит.

Также помните часть выше о соединении ассоциаций автоматически для Вашего? У Вас нет этого с NoTracking, поэтому при загрузке данных в нескольких пакетах у Вас не будет ссылки на между ними:

ObjectQuery<Dog> oDogQuery = (ObjectQuery<Dog>)(from dog in YourContext.DogSet select dog);
oDogQuery.MergeOption = MergeOption.NoTracking;
List<Dog> dogs = oDogQuery.ToList();

ObjectQuery<Person> oPersonQuery = (ObjectQuery<Person>)(from o in YourContext.PersonSet  select o);
oPersonQuery.MergeOption = MergeOption.NoTracking;
    List<Person> owners = oPersonQuery.ToList();

В этом случае ни у какой собаки не будет своего.Owner набора свойств.

Некоторые вещи иметь в виду, когда Вы пытаетесь оптимизировать производительность.

Никакая ленивая загрузка, что я должен сделать?

Это может рассматриваться как скрытое благословение. Конечно, это является раздражающим для загрузки всего вручную. Однако это сокращает число вызовов к дб и вынуждает Вас думать о том, когда необходимо загрузить данные. Чем больше можно загрузиться в одном вызове базы данных, тем лучше. Это было всегда верно, но это осуществляется теперь с этой 'функцией' EF.

Конечно, можно звонить если (! ObjectReference. IsLoaded) ObjectReference. Загрузка (); если Вы хотите, но лучшая практика должна вынудить платформу загрузить объекты, Вы знаете, что Вам будет нужно в одном выстреле. Это - то, где дискуссия о параметрическом Включает, начинает иметь смысл.

Позволяет говорят, что у Вас есть Вы объект Собаки

public class Dog
{
    public Dog Get(int id)
    {
        return YourContext.DogSet.FirstOrDefault(it => it.ID == id );
    }
}

Это - тип функции, с которой Вы работаете все время. Это называют со всех концов места и как только у Вас есть тот объект Собаки, Вы сделаете совсем другие вещи к нему в различных функциях. Во-первых, это должно быть предварительно скомпилировано, потому что Вы будете называть это очень часто. Во-вторых, каждый различные страницы захочет иметь доступ к различному подмножеству данных Собаки. Некоторые захотят Владельца, некоторые FavoriteToy, и т.д.

Конечно, Вы могли назвать Загрузку () для каждой ссылки, в которой Вы нуждаетесь каждый раз, когда Вам нужен тот. Но это генерирует вызов к базе данных каждый раз. Плохая идея. Таким образом вместо этого, каждая страница попросит данные, которые она хочет видеть когда это первый запрос на объект Собаки:

    static public Dog Get(int id) { return GetDog(entity,"");}
    static public Dog Get(int id, string includePath)
{
        string query = "select value o " +
            " from YourEntities.DogSet as o " +
74
задан 3 revs, 3 users 100% 13 February 2009 в 22:02
поделиться

1 ответ

Хотя это и информативно, я думаю, что может быть более полезным поделиться тем, как все это вписывается в полную архитектуру решения. Пример. Получено решение, показывающее, где вы используете как наследование EF, так и альтернативу, чтобы показать разницу в их производительности.

1
ответ дан 24 November 2019 в 12:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: