Существует отличный способ сделать это в EF 4.0, используя ObjectStateManager
Во-первых, вам нужно создать частичный классдля вашего ObjectContext и подпишитесь на ObjectContext.SavingChanges Event .Лучшее место для подписки на это событие - внутри метода OnContextCreated.Этот метод вызывается конструктором объекта контекста и перегружает конструктор, который является частичным методом без реализации:
partial void OnContextCreated() {
this.SavingChanges += Context_SavingChanges;
}
Теперь фактический код, который будет выполнять эту работу:
void Context_SavingChanges(object sender, EventArgs e) {
IEnumerable<ObjectStateEntry> objectStateEntries =
from ose
in this.ObjectStateManager.GetObjectStateEntries(EntityState.Added
| EntityState.Modified)
where ose.Entity != null
select ose;
foreach (ObjectStateEntry entry in objectStateEntries) {
ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues
.DataRecordInfo.FieldMetadata;
FieldMetadata modifiedField = fieldsMetaData
.Where(f => f.FieldType.Name == "LastModifiedUser").FirstOrDefault();
if (modifiedField.FieldType != null) {
string fieldTypeName = modifiedField.FieldType.TypeUsage.EdmType.Name;
if (fieldTypeName == PrimitiveTypeKind.String.ToString()) {
entry.CurrentValues.SetString(modifiedField.Ordinal, Lookupuser());
}
}
}
}
Объяснение кода :
Этот код находит любые добавленные или измененные записи, которые имеют свойство LastModifiedUser , а затем обновляет ихсвойство со значением, полученным из вашего пользовательского Lookupuser () метода.
В блоке foreach запрос в основном детализирует CurrentValues каждой записи.Затем, используя метод Где , он просматривает имена каждого элемента FieldMetaData для этой записи, выбирая только те, чьи Имя равно LastModifiedUser .Затем оператор if проверяет, что свойство LastModifiedUser является полем String ;затем он обновляет значение поля.
Другой способ подключить этот метод (вместо подписки на событие SavingChanges ) - переопределить метод ObjectContext.SaveChanges .
Кстати, приведенный выше код принадлежит Джули Лерман из ее Programming Entity Framework book.
РЕДАКТИРОВАТЬ для реализации POCO с самотрекингом:
Если у вас есть POCO с самотрекингом, то я бы сначала изменил шаблон T4 для вызоваМетод OnContextCreated ().Если вы посмотрите на файл ObjectContext.tt, есть метод Initialize () , который вызывается всеми конструкторами, поэтому хороший кандидат для вызова нашего метода OnContextCreated (), поэтому все, что нам нужно сделать, этоизмените файл ObjectContext.tt следующим образом:
private void Initialize()
{
// Creating proxies requires the use of the ProxyDataContractResolver and
// may allow lazy loading which can expand the loaded graph during serialization.
ContextOptions.ProxyCreationEnabled = false;
ObjectMaterialized += new ObjectMaterializedEventHandler(HandleObjectMaterialized);
// We call our custom method here:
OnContextCreated();
}
И это вызовет вызов OnContextCreated () при создании контекста.
Теперь, если вы поместите свои POCO за границу службы, это означает, что ModifiedUserName должно поставляться с остальными данными от вашего потребителя службы WCF.Вы можете либо предоставить им это свойство LastModifiedUser для обновления, либо, если оно сохраняется в другом свойстве, и вы хотите обновить LastModifiedUser из этого свойства, то вы можете изменить 2-й код следующим образом:
foreach (ObjectStateEntry entry in objectStateEntries) {
ReadOnlyCollection fieldsMetaData = entry.CurrentValues
.DataRecordInfo.FieldMetadata;
FieldMetadata sourceField = fieldsMetaData
.Where(f => f.FieldType.Name == "YourPropertyName").FirstOrDefault();
FieldMetadata modifiedField = fieldsMetaData
.Where(f => f.FieldType.Name == "LastModifiedUser").FirstOrDefault();
if (modifiedField.FieldType != null) {
string fieldTypeName = modifiedField.FieldType.TypeUsage.EdmType.Name;
if (fieldTypeName == PrimitiveTypeKind.String.ToString()) {
entry.CurrentValues.SetString(modifiedField.Ordinal,
entry.CurrentValues[sourceField.Ordinal].ToString());
}
}
}
Надеюсь, это поможет.