Проблема обновления сущности POCO в шаблоне репозитория - PullRequest
4 голосов
/ 15 января 2011

У меня есть проблема в моем UserRepository, в которой я хочу обновить пользователя.Я не хочу, чтобы некоторые поля обновлялись, например пароль, если не указано иное.Например, когда я передаю пользователя из представления в службу в хранилище, он отправляет пользователю пустую или пустую строку пароля.Этот ноль записывается в базу данных (что мне не нужно).

Как мне справиться с такой ситуацией?

Домен

public class User
{
    public int UserId { get; set; }

    public string Email { get; set; }
    public string Password { get; set; }
}

Репозиторий

    public User Save(User user)
    {
        if (user.UserId > 0)
        {
            User dbUser = context.Users.FirstOrDefault(u => u.UserId == user.UserId);
            //What do I do here?
        }
        context.Users.AddObject(user);
        context.SaveChanges();
        return user;
    }

Допустим, в этом случае мое мнение позволяет мне изменять только Email, поэтому единственное, что возвращается в метод Save(), это:user.UserId и user.Email, в то время как user.Password равно нулю.В моем случае база данных выдает ошибку, потому что пароль должен быть обнуляемым.

Ответы [ 3 ]

10 голосов
/ 15 января 2011

Отдельный сценарий POCO (вы не будете загружать пользователя из БД до обновления):

Вы можете выборочно указать, какие свойства должны быть обновлены:

public User Save(User user)     
{         
    if (user.UserId == 0)         
    {             
        context.Users.AddObject(user);         
    }
    else
    {
        context.Users.Attach(user);
        ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(user);
        entry.SetModifiedProperty("Email");
    }

    context.SaveChanges();         
    return user;     
}

Вытакже может создать две перегрузки вашего Save метода.Первый обновит весь объект, второй обновит только явно выбранные свойства:

public User Save(User user)     
{         
    if (user.UserId == 0)         
    {             
        context.Users.AddObject(user);         
    }
    else
    {
        context.Users.Attach(user);
        context.ObjectStateManager.ChangeObjectState(user, EntityState.Modified);        
    }

    context.SaveChanges();         
    return user;     
}

public User Save(User user, IEnumerable<Expression<Func<User, object>>> properties)     
{         
    if (user.UserId == 0)         
    {             
        context.Users.AddObject(user);         
    }
    else
    {
        context.Users.Attach(user);
        ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(user);
        foreach(var selector in properties)
        {
            string propertyName = PropertyToString(selector.Body);
            entry.SetModifiedProperty(propertyName);
        }
    }

    context.SaveChanges();         
    return user;     
}

// Doesn't work for navigation properties!
private static string PropertyToString(Expression selector)
{
    if (selector.NodeType == ExpressionType.MemberAccess)
    {
        return ((selector as MemberExpression).Member as PropertyInfo).Name;
    }

    throw new InvalidOperationException();
}

Вы вызовете вторую перегрузку следующим образом:

userRepository.Save(user, new List<Expression<Func<User, object>>> 
    { 
        u => u.Email 
    });

Прикрепленный сценарий (вы загрузитепользователь из БД перед обновлением):

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

public User Save(User user, Action<User, User> updateStrategy)                                
{                                  
    if (user.UserId > 0)                                  
    {
        User dbUser = context.Users.FirstOrDefault(u => u.UserId == user.UserId);
        updateStrategy(dbUser, user);                                                                        
    }        
    else
    {                          
        // New object - all properties should be saved
        context.Users.AddObject(user);
    }

    context.SaveChanges();                                  
    return user;                              
}  

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

var user = GetUpdatedUserFromSomewhere();
repository.Save(user, (dbUser, mergedUser) => 
    {
        dbUser.Email = mergedUser.Email;
    });

В любом случае, несмотря на мои примеры, вам обязательно стоит подумать о публикации Дарина и специальных ModelViews для обновления.

1 голос
/ 15 января 2011

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

[HttpPost]
public ActionResult Update(UserViewModel model) { ... }

вместо:

[HttpPost]
public ActionResult Update(User model) { ... }

Внутри действия контроллера вы можете отобразить между моделью представления и моделью. AutoMapper - отличный инструмент, который может упростить эту задачу.

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

0 голосов
/ 15 января 2011

Вы можете сделать это?

public User Save(User user)
    {
        if (user.UserId > 0)
        {
            User dbUser = context.Users.FirstOrDefault(u => u.UserId == user.UserId);
            //What do I do here?
            dbUser.Email = user.Email
            user = dbUser;
        }
        else
        {
            context.Users.AddObject(user);
        }
        context.SaveChanges();
        return user;
    }
...