Использование Dynamic Data совместно с Entity Framework 5



    Добрый день!

    Как известно, уже вышла релиз-версия .NET Framework 4.5, а также стала доступна для загрузки финальная версия Visual Studio 2012.

    Познакомиться с новой Visual Studio я успел еще с beta версии, а после релиза начал использовать rtm релиз Visual Studio и .NET Framework 4.5 в реальной работе.
    В новую версию .NET Framework входит так же новая версия Entity Framework. Уже пятая. Точнее она туда не совсем входит — у меня при создании проекта файлы подгружаются из репозитория NuGet. Но в любом случае в новом проекте используется именно 5 версия библиотеки.

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

    Что нового в Entity Framework 5.0


    • Значительное повышение производительности — до 67%.
    • Поддержка свойств типа enum, которая доступна для использования во всех подходах: Model, Database и Code First.
    • Поддержка пространственные типов данных с использованием DbGeography и DbGeometry типов. Они также доступны в Model, Database и Code First подходах.
    • При генерации кода редактор Visual Studio теперь будет использовать по умолчнию DbContext в качестве базового класса для новых моделей. Это означает, что любые новые модели, созданные с помощью конструктора EF будут создавать производные от DbContext и POCO классы по умолчанию. При это остается возможность вернуться к генерации кода на основе ObjectContext если это необходимо. Существующие модели не будет автоматически изменяться в поколение код DbContext.
    • Функции теперь могут возвращающать пользовательские таблицы при применении подхода Database First.

    Это не полный перечень, но и эти возможности заинтересовали меня достаточно сильно.
    Более подробно о нововведениях можно узнать тут.

    Суть задачи


    Во многих своих проектах для управления данными я применяю решение созданное на основе ASP.NET Dynamic Data (о том как именно можно применять это решение, и в целом инструменты реализующие технологию скаффолдинга — я писал ранее). Как уже было сказано, в новой версии Entity Framework даже при использовании режима Database First теперь генерирует контекст на основе класса DbContext, а не ObjectContext, как было раньше. Dynamic Data предполагает же, что в качестве базового класса контекста используется именно ObjectContext.

    В связи с этим, для корректной работы Dynamic Data пришлось немного изменить инициализацию контекста и работу некоторых контролов. Очень хорошую статью по этому поводу я нашел в блоге Пранава Растоги,

    Думаю что эта информация пригодится тем, кто использует Dynamic Data и планирует переходить на новую версию Entity Framework.

    Настройка ASP.NET Dynamic Data для взаимодействия с контекстом на основе DbContext


    Для того, что бы Dynamic Data корректно работал с новым форматом необходимо сделать три простых шага.

    1. Измените код Global.asax в корне проекта

            DefaultModel.RegisterContext(() =>
            {
                    return ((IObjectContextAdapter)new YourContextType()).ObjectContext;
            }, new ContextConfiguration() { ScaffoldAllTables = true });
    


    2. Измените код контрола ManyToMany.ascx.cs в директории Dynamicdata\Fieldtemplates

            protected override void OnDataBinding(EventArgs e)
            {
                base.OnDataBinding(e);
     
                object entity;
                ICustomTypeDescriptor rowDescriptor = Row as ICustomTypeDescriptor;
                if (rowDescriptor != null)
                {
                    // Get the real entity from the wrapper
                    entity = rowDescriptor.GetPropertyOwner(null);
                }
                else
                {
                    entity = Row;
                }
     
                // Get the collection and make sure it's loaded
                var entityCollection = Column.EntityTypeProperty.GetValue(entity, null);
                var realEntityCollection = entityCollection as RelatedEnd;
                if (realEntityCollection != null && !realEntityCollection.IsLoaded)
                {
                    realEntityCollection.Load();
                }
     
     
                // Bind the repeater to the list of children entities
                Repeater1.DataSource = entityCollection;
                Repeater1.DataBind();
            }
     
            public override Control DataControl
            {
                get
                {
                    return Repeater1;
                }
            }
    


    3. Измените код контрола ManyToMany_Edit.ascx.cs в директории Dynamicdata\Fieldtemplates


    
            protected ObjectContext ObjectContext { get; set; }
     
            public void Page_Load(object sender, EventArgs e)
            {
                // Register for the DataSource's updating event
                EntityDataSource ds = (EntityDataSource)this.FindDataSourceControl();
     
                ds.ContextCreated += (_, ctxCreatedEnventArgs)
                             => ObjectContext = ctxCreatedEnventArgs.Context;
     
                // This field template is used both for Editing and Inserting
                ds.Updating += DataSource_UpdatingOrInserting;
                ds.Inserting += DataSource_UpdatingOrInserting;
            }
     
            void DataSource_UpdatingOrInserting(object sender, EntityDataSourceChangingEventArgs e)
            {
                MetaTable childTable = ChildrenColumn.ChildTable;
     
                // Comments assume employee/territory for illustration, but the code is generic
                if (Mode == DataBoundControlMode.Edit)
                {
                    ObjectContext.LoadProperty(e.Entity, Column.Name);
                }
     
                // Get the collection and make sure it's loaded
                dynamic entityCollection = Column.EntityTypeProperty.GetValue(e.Entity, null);
     
                // Go through all the territories (not just those for this employee)
                foreach (dynamic childEntity in childTable.GetQuery(e.Context))
                {
     
                    // Check if the employee currently has this territory
                    var isCurrentlyInList = ListContainsEntity(childTable, entityCollection, childEntity);
     
                    // Find the checkbox for this territory, which gives us the new state
                    string pkString = childTable.GetPrimaryKeyString(childEntity);
                    ListItem listItem = CheckBoxList1.Items.FindByValue(pkString);
                    if (listItem == null)
                        continue;
     
                    // If the states differs, make the appropriate add/remove change
                    if (listItem.Selected)
                    {
                        if (!isCurrentlyInList)
                            entityCollection.Add(childEntity);
                    }
                    else
                    {
                        if (isCurrentlyInList)
                            entityCollection.Remove(childEntity);
                    }
                }
            }
     
            private static bool ListContainsEntity(MetaTable table, IEnumerable<object> list, object entity)
            {
                return list.Any(e => AreEntitiesEqual(table, e, entity));
            }
     
            private static bool AreEntitiesEqual(MetaTable table, object entity1, object entity2)
            {
                return Enumerable.SequenceEqual(
                            table.GetPrimaryKeyValues(entity1), table.GetPrimaryKeyValues(entity2));
            }
     
            protected void CheckBoxList1_DataBound(object sender, EventArgs e)
            {
                MetaTable childTable = ChildrenColumn.ChildTable;
     
                // Comments assume employee/territory for illustration, but the code is generic
     
                IEnumerable<object> entityCollection = null;
     
                if (Mode == DataBoundControlMode.Edit)
                {
                    object entity;
                    ICustomTypeDescriptor rowDescriptor = Row as ICustomTypeDescriptor;
                    if (rowDescriptor != null)
                    {
                        // Get the real entity from the wrapper
                        entity = rowDescriptor.GetPropertyOwner(null);
                    }
                    else
                    {
                        entity = Row;
                    }
     
                    // Get the collection of territories for this employee and make sure it's loaded
                    entityCollection = (IEnumerable<object>)Column.EntityTypeProperty.GetValue(entity, null);
                    var realEntityCollection = entityCollection as RelatedEnd;
                    if (realEntityCollection != null && !realEntityCollection.IsLoaded)
                    {
                        realEntityCollection.Load();
                    }
                }
     
                // Go through all the territories (not just those for this employee)
                foreach (object childEntity in childTable.GetQuery(ObjectContext))
                {
                    // Create a checkbox for it
                    ListItem listItem = new ListItem(
                        childTable.GetDisplayString(childEntity),
                        childTable.GetPrimaryKeyString(childEntity));
     
                    // Make it selected if the current employee has that territory
                    if (Mode == DataBoundControlMode.Edit)
                    {
                        listItem.Selected = ListContainsEntity(childTable, entityCollection, childEntity);
                    }
     
                    CheckBoxList1.Items.Add(listItem);
                }
            }
     
            public override Control DataControl
            {
                get
                {
                    return CheckBoxList1;
                }
            }
     
    


    После проделанных изменений Dynamic Data как ни в чем не бывало продолжит работать и вы сможете и дальше наслаждаться процессом разработки.



    Источники


    Ниже привожу список ссылок, по теме статьи. Надеюсь они окажутся полезными.

    Поделиться публикацией

    Похожие публикации

    Комментарии 4
      –1
      В первом заголовке «Что нового в Entity Frameowrk 5.0» ошибка в слове Famework.
        0
        Спасибо, исправил!
        0
        Для того, что бы Dynamic Data корректно работал с новым форматом необходимо сделать три простых шага

        … и дальше простыня кода.
        Напомнило: «Чтобы нарисовать сову, нужно сделать два шага: (1) Нарисовать 2 овала и (2) дорисовать сову.
          0
          На самом деле нужно всего лишь изменить один контрол и инициализацию контекста. Так что все действительно достаточно просто.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое