- PVSM.RU - https://www.pvsm.ru -
Добрый день!
Как известно, уже вышла релиз-версия .NET Framework 4.5 [1], а также стала доступна для загрузки финальная версия Visual Studio 2012 [2].
Познакомиться с новой Visual Studio я успел еще с beta версии, а после релиза начал использовать rtm релиз Visual Studio и .NET Framework 4.5 в реальной работе.
В новую версию .NET Framework входит так же новая версия Entity Frameowrk. Уже пятая. Точнее она туда не совсем входит — у меня при создании проекта файлы подгружаются из репозитория NuGet. Но в любом случае в новом проекте используется именно 5 версия библиотеки.
Прежде чем продолжить, хочу вкратце рассказать, что же нового появилось в EF5 и почему я решил начать использовать эту версию.
Это не полный перечень, но и эти возможности заинтересовали меня достаточно сильно.
Более подробно о нововведениях можно узнать тут [3].
Во многих своих проектах для управления данными я применяю решение созданное на основе ASP.NET Dynamic Data (о том как именно можно применять это решение, и в целом инструменты реализующие технологию скаффолдинга — я писал ранее [4]). Как уже было сказано, в новой версии Entity Framework даже при использовании режима Database First теперь генерирует контекст на основе класса DbContext [5], а не ObjectContext [6], как было раньше. Dynamic Data предполагает же, что в качестве базового класса контекста используется именно ObjectContext.
В связи с этим, для корректной работы Dynamic Data пришлось немного изменить инициализацию контекста и работу некоторых контролов. Очень хорошую статью по этому поводу я нашел в блоге Пранава Растоги [7],
Думаю что эта информация пригодится тем, кто использует Dynamic Data и планирует переходить на новую версию Entity Framework.
Для того, что бы Dynamic Data корректно работал с новым форматом необходимо сделать три простых шага.
DefaultModel.RegisterContext(() =>
{
return ((IObjectContextAdapter)new YourContextType()).ObjectContext;
}, new ContextConfiguration() { ScaffoldAllTables = true });
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;
}
}
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 как ни в чем не бывало продолжит работать и вы сможете и дальше наслаждаться процессом разработки.
Ниже привожу список ссылок, по теме статьи. Надеюсь они окажутся полезными.
Автор: Ernado
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/net/13564
Ссылки в тексте:
[1] .NET Framework 4.5: http://www.microsoft.com/net
[2] Visual Studio 2012: http://www.microsoft.com/visualstudio/11/en-us
[3] тут: http://blogs.msdn.com/b/adonet/archive/2012/08/15/ef5-released.aspx
[4] я писал ранее: http://habrahabr.ru/post/141207/
[5] DbContext: http://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext.aspx
[6] ObjectContext: http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.aspx
[7] блоге Пранава Растоги: http://blogs.msdn.com/b/webdev/archive/2012/08/15/using-dynamic-data-with-entity-framework-dbcontext.aspx
[8] msdn.microsoft.com/ru-RU/data/ef: http://msdn.microsoft.com/ru-RU/data/ef
[9] andrey.moveax.ru/news/2012-05-17/entity-framework-5-0-release-candidate/: http://andrey.moveax.ru/news/2012-05-17/entity-framework-5-0-release-candidate/
Нажмите здесь для печати.