2013-07-10 2 views
0

У меня есть набор репозиториев на основе Entity Framework (EF), некоторые из которых связаны с объектами, которые могут быть мягкими (не все из них могут). Объекты автоматически генерируются EF. До сих пор у меня есть:Мягкий репозиторий при сохранении свойства private

  • объекты, которые могут быть мягкими удалены реализовать интерфейс ICanBeSoftDeleted:

    public interface ICanBeSoftDeleted 
    { 
        bool IsDeleted { get; set; } 
    } 
    
  • Для этих лиц я использую репозитории, которые реализуют ISoftDeleteRepository:

    public interface ISoftDeleteRepository<T> : IRepository<T> where T : class, ICanBeSoftDeleted  
    { 
        void SoftDelete(T entity); 
        void SoftDelete(Expression<Func<T, bool>> where); 
    } 
    
  • У меня также есть базовый класс для этих, SoftDeleteRepositoryBase, который расширяет базу RepositoryBase и добавляет методы мягкого удаления:

    public abstract class SoftDeleteRepositoryBase<T> : RepositoryBase<T> where T : class, ICanBeSoftDeleted 
    { 
        public virtual void SoftDelete(T entity) 
        { 
         entity.IsDeleted = true; 
         Update(entity); 
        } 
    
        public virtual void SoftDelete(Expression<Func<T, bool>> where) 
        { 
         var entitiesToDelete = GetMany(where).AsEnumerable(); 
         foreach (T entity in entitiesToDelete) 
         { 
          this.Delete(entity); 
         } 
        } 
    } 
    

Это все работает отлично. Однако этот код является частью внутренней библиотеки, распространяемой среди пользователей, напрямую вызывающих репозитории, и я не хочу, чтобы они меняли свойство IsDeleted, только читали его или удаляли объекты, вызывающие метод. Сейчас они могут это сделать, потому что сеттер является публичным.

Как я могу изменить свой код для этого? Я не могу изменить ICanBeSoftDeleted и удалить setter, потому что тогда я не смог бы его модифицировать из SoftDeleteRepositories.

Благодаря

Update: на данный момент я решил проблему путем удаления «набор» из интерфейса и установки значения в хранилище с отражением:

public virtual void Delete(T entity) 
{ 
    PropertyInfo propertyInfo = entity.GetType().GetProperty("IsDeleted"); 
    propertyInfo.SetValue(entity, true); 

    Update(entity); 
} 

Однако это чувствует себя как патч для меня, я не думаю, что он решает проблему большего дизайна ...

ответ

1

Вам нужно будет удерживать классы EF за стеной и отображать POCO; или набирать их как интерфейс (который не объявляет сеттер) при передаче их потребителям.

Второй вариант оставляет объекты открытыми, чтобы удалить флаг, установленный отражением.

EDIT: Дальнейший анализ кода, который размещен приводит к следующему вопросу:

Вы намерены дать потребителям вашего API возможность декларировать Хранилище?

Казалось бы, мудрее разоблачить только не общие хранилища - например. CustomerRepository, UserRepository, PurchaseRepository в вашем API.

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

+0

Идея заключается в том, чтобы только разоблачить не -генерированные репозитории. Проблема в том, что если ICustomerRepository является общедоступным, то абстрактный класс RepositoryBase, который он расширяет, также должен быть общедоступным. Это позволило бы людям создавать свои собственные репозитории, расширяя сам репозиторий, и я не хочу ... каких-либо идей вокруг этого? – chuwik

+0

Я хочу использовать генерируемые EF POCOs как DTO, поэтому я бы не пошел на первый вариант. Я подумал о втором, удалив «набор» из интерфейса ICanBeSoftDeleted и установив его с отражением в репозитории. Однако это показалось мне немного «взломанным» ... Были ли проблемы с производительностью? – chuwik

+0

Это не мешает потребителю возиться с удаленным флагом, используя ту же технику, что и вы. –

0
public bool IsDeleted 
{ 
    get 
    { 
     return this.isDeleted; 
    } 
    //you can use protected if you intend to inherit the property and override it 
    private set 
    { 
     this.isDeleted= value; 
    } 
} 

EDIT: Здесь я опишу создание имущества, которое может быть использовано для обнаружения ли свойство или функциональность вызывается из действительного места.

  1. Вам нужна защищенная переменная в вашем классе, где вы хотите проверить свойство IsDeleted. Для простоты предположим, что это строка и пусть она будет называться softDeleteKey.

  2. Для этого вам нужен публичный сеттер, назовем его setSoftDeleteKey.

  3. Вам нужна частная функция, которая проверяет правильность вашего ключа, возвращает true, если она действительна и ложна, если она недействительна. Назовем это validateSoftDeleteKey.

  4. Реализовать свойства только для чтения под названием isSoftDeleteKeyValid который будет вызывать функцию, описанную в 3.

  5. В вашем IsDeleted собственности проверить isSoftDeleteKeyValid. Если он возвращает false, выведите исключение. Если IsDeleted был успешным, установите softDeleteKey на недопустимое значение.

  6. В вашем методе SoftDelete вызывается setSoftDeleteKey с допустимым значением перед установкой свойства IsDeleted. Если возникает исключение, вызовите setSoftDeleteKey с недопустимым значением.

Надеюсь, эти идеи помогут вам.

+0

Создание закрытого или защищенного сеттера мешает мне изменять его значение в методе SoftDelete() репозитория. – chuwik

+0

Затем вы можете создать свойство bool, которое было бы истинным тогда и только тогда, когда установка IsDeleted является действительной. В свойстве IsDeleted забудьте о защищенном ключевом слове и throw и исключении в сеттер, если вызывающий объект был вызван из недопустимого места. Внесите исключение InvalidReferenceToIsDeleteSetterException и выбросьте это ... –

+0

Как я могу это сделать? Метод будет вызван как внутри сборки, так и за ее пределами с помощью внешних инструментов, которые ссылаются на нее. Не могли бы вы показать примерный код? – chuwik

0

Не могли бы вы это сделать, проверив, является ли объект реализацией ICanBeSoftDeleted внутри вашего хранилища RepositoryBase?

Используя расширение отсюда: http://bradhe.wordpress.com/2010/07/27/how-to-tell-if-a-type-implements-an-interface-in-net/

public static class TypeExtensions 
{ 
    //http://bradhe.wordpress.com/2010/07/27/how-to-tell-if-a-type-implements-an-interface-in-net/ 
    public static bool IsImplementationOf(this Type baseType, Type interfaceType) 
    { 
     return baseType.GetInterfaces().Any(interfaceType.Equals); 
    } 
} 

public interface IRepository<T> 
{ 
    void Delete(T entity); 
} 

public class RepositoryBase<T> : IRepository<T> where T : class 
{ 
    public void Delete(T entity) 
    { 
     if (typeof(T).IsImplementationOf(typeof(ICanBeSoftDeleted))) 
     { 
      ((ICanBeSoftDeleted)entity).IsDeleted = true; 
      //etc 
     } 
     else 
     { 
      //hard delete 
     } 
    } 
} 

public class Customer : ICanBeSoftDeleted 
{ 
    public bool IsDeleted { get; set; } 
} 

public class UOW 
{ 

    private IRepository<T> GetRepository<T>() 
    { 
     return (IRepository<T>)new RepositoryBase<T>(); 
    } 

    public IRepository<Customer> CustomerRepository 
    { 
     get 
     { 
      return GetRepository<Customer>(); 
     } 
    } 
} 

public interface ICanBeSoftDeleted 
{ 
    bool IsDeleted { get; set; } 
} 
+0

Объекты, которые могут быть мягко удалены, также могут быть удалены. Метод жесткой удаления находится в RepositoryBase, а метод мягкого удаления является добавлением только на SoftRepositoryBase, так что только эти объекты могут его использовать. К сожалению, этот подход нельзя использовать. – chuwik

+0

Таким образом, пользователи могут вызывать SoftDelete(), который устанавливает IsDeleted в true (и вызывает обновление), но вы не хотите, чтобы они напрямую устанавливали IsDeleted в true. Это потому, что вы беспокоитесь о том, что они могут забыть назвать Update()? – Colin

+0

Нет, это потому, что мягкое удаление чего-то не просто; Я упростил метод для примера, но я также хочу всегда регистрировать активность, возможно, делать другие вещи, когда сущность удаляется и т. Д. В основном я хочу контролировать бизнес-логику вокруг него. – chuwik

0

Как об этом:

public interface ICanBeSoftDeleted 
{ 
    bool IsDeleted { get; } 
} 

public abstract class CanBeSoftDeleted : ICanBeSoftDeleted 
{ 
    private bool _IsDeleted; 
    public bool IsDeleted 
    { 
     get { return _IsDeleted; } 
    } 

    internal void SetIsDeleted(bool value) { _IsDeleted = value; } 
} 

Затем модели могут наследовать от абстрактного класса вместо

Смежные вопросы