Свойства только для чтения в EF 4.1 - PullRequest
5 голосов
/ 21 апреля 2011

Я столкнулся с ситуацией, когда мне нужно иметь свойство EF readonly в случае «оптимистического обновления» (вы не загружаете текущее состояние вашего доменного объекта из базы данных, чтобы проверить, какие свойства действительно изменены. Вы просто устанавливаете свой объект как измененный и обновите его до базы данных. В этом случае вы избегаете избыточных операций выбора и объединения).

Вы не можете написать что-то вроде этого: DataContext.Entry(entity).Property(propertyName).IsModified = false;, потому что установка значения 'false' не поддерживается, и вы получите исключение. (в EF 4.1)

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

Что вы думаете об этом?

public abstract class RepositoryBase<T> where T : class
{   
 private const string MethodReferenceErrorFormat = "Expression '{0}' refers to a method, not a property.";
 private const string FieldReferenceErrorFormat = "Expression '{0}' refers to a field, not a property.";

 protected IList<PropertyInfo> _readOnlyProperties;
        /// <summary>
        /// This method is used to register readonly property for Entity.
        /// </summary>
        /// <param name="propertyLambda">Entity property as LambdaExpression</param>
        protected void RegisterReadOnlyProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda)
        {
            Guard.ArgumentNotNull(propertyLambda, "propertyLambda");

            var propertyMember = propertyLambda.Body as MemberExpression;
            if (propertyMember == null)
            {
                var exceptionMessage = string.Format(MethodReferenceErrorFormat, propertyLambda);
                throw new ArgumentException(exceptionMessage);
            }

            var propertyInfo = propertyMember.Member as PropertyInfo;
            if (propertyInfo == null)
            {
                var exceptionMessage = string.Format(FieldReferenceErrorFormat, propertyLambda);
                throw new ArgumentException(exceptionMessage);
            }

            _readOnlyProperties.Add(propertyInfo);
        }

         /// <summary>
         /// This method is used to attach domain object to DbContext and mark it as modified to save changes.
         /// </summary>
         /// <param name="entity">Detached entity</param>
        public void SetModified(T entity)
        {
            Guard.ArgumentNotNull(entity, "entity");

            //Mark whole entity as Modified, when collection of readonly properties is empty.
            if(_readOnlyProperties.Count == 0)
            {
                DataContext.Entry(entity).State = EntityState.Modified;
                return;
            }

             //Attach entity to DbContext.
             _dbSet.Attach(entity);

            //Mark all properties except readonly as Modified.
            var allProperties = entity.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            var propertiesForUpdate = allProperties.Except(_readOnlyProperties);
            foreach (var propertyInfo in propertiesForUpdate)
            {
                DataContext.Entry(entity).Property(propertyInfo.Name).IsModified = true;
            }
        }

1 Ответ

17 голосов
/ 22 апреля 2011

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

Я написал ответ для EFv4 , но его можно легко изменить на EFv4.1:

public void Update(T entity, params Expression<Func<T, object>>[] properties)
{
    _dbSet.Attach(entity);
    DbEntityEntry<T> entry = _context.Entry(entity);
    foreach (var selector in properties)
    {
        entry.Property(selector).IsModified = true;
    }
}

Вы будете называть это как:

repo.Update(entity, e => e.Name, e => e.Description);
...