
Добрый день!
Как известно, уже вышла релиз-версия .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 как ни в чем не бывало продолжит работать и вы сможете и дальше наслаждаться процессом разработки.
Источники
Ниже привожу список ссылок, по теме статьи. Надеюсь они окажутся полезными.
