2009-11-02 3 views
3

Я вызвал у меня некоторую проблему с моим уровнем доступа к данным. В этом конкретном случае у меня есть таблица, которая содержит потенциально 5 типов «сущности». Это, в основном, компания, клиент, сайт и т. Д. Тип продиктован PositionTypeId в таблице. Все они находятся в одной таблице, так как все они имеют одну и ту же структуру данных; PositionId, описание и код.Нужна помощь в сортировке основной абстрактной головной боли в моем DAL

У меня есть основной абстрактный класс следующим образом:

public abstract class PositionProvider<T> : DalProvider<T>, IDalProvider where T : IPositionEntity 
{ 
    public static PositionProvider<T> Instance 
    { 
     get 
     { 
      if (_instance == null) 
      { 
       // Create an instance based on the current database type 
      } 
      return _instance; 
     } 
    } 
    private static PositionProvider<T> _instance; 

    public PositionType PositionType 
    { 
     get 
     { 
      return _positionType; 
     } 
    } 
    private PositionType _positionType; 

    // Gets a list of entities based on the PositionType enum's value. 
    public abstract List<T> GetList(); 

    internal void SetPositionType(RP_PositionType positionType) 
    { 
     _positionType = positionType; 
    } 

} 

Я хочу, чтобы затем иметь возможность поставить все общий код в пределах класса inherting, который либо SQL или Oracle на основе. Это моя реализация SQL:

public class SqlPositionProvider<T> : PositionProvider<T> where T : IPositionEntity 
{ 
     public override List<T> GetList() 
     { 
      int positionTypeId = (int)this.PositionType; 
      using (SqlConnection cn = new SqlConnection(Globals.Instance.ConnectionString)) 
      { 
       SqlCommand cmd = new SqlCommand("Get_PositionListByPositionTypeId", cn); 
       cmd.Parameters.Add("@PositionTypeId", SqlDbType.Int).Value = positionTypeId; 
       cmd.CommandType = CommandType.StoredProcedure; 
       cn.Open(); 
       return this.GetCollectionFromReader(this.ExecuteReader(cmd)); 
      } 
     } 
} 

Я затем создать класс для каждого типа следующим образом (это не является CustomerProvider в качестве примера):

public class CustomerProvider 
{ 
    public static PositionProvider<CustomerEntity> Instance 
    { 
     get 
     { 
      if ((int)PositionProvider<CustomerEntity>.Instance.PositionType == 0) 
      { 
       PositionProvider<CustomerEntity>.Instance.SetPositionType(PositionType.Customer); 
      } 
      return PositionProvider<CustomerEntity>.Instance; 
     } 
    } 
} 

Это все работает сказочно ... до тех пор, Я понял, что у меня есть определенные функции, которые связаны конкретно с определенными типами позиций. То есть Мне нужно, чтобы все клиенты (который является IPositionType) на основе прав пользователя.

Так что мне нужно добавить еще один абстрактный метод:

public abstract List<CustomerEntity> GetCustomersByUserPermission(Guid userId); 

Теперь, очевидно, я не хочу, чтобы это в моем PositionProvider абстрактного класса, как это будет означать, что метод будет появляться при работе с сайтом/поставщика компании ,

Как добавить этот и другие дополнительные методы без дублирования кода в SqlPositionProvider?

Edit:

Единственная идея, которую я придумал, чтобы отделить PositionProvider из в общей собственности CustomerProvider, SiteProvider, etcProvider:

public abstract class CustomerProvider 
{ 

    public CustomerProvider() 
    { 
     this.Common.SetPositionType(PositionType.Customer); 
    } 

    public PositionProvider<CustomerEntity> Common 
    { 
     get 
     { 
      if (_common == null) 
      { 
       DalHelper.CreateInstance<PositionProvider<CustomerEntity>>(out _common); 
      } 
      return _common; 
     } 
    } 
    private PositionProvider<CustomerEntity> _common; 

    public static CustomerProvider Instance 
    { 
     get 
     { 
      if (_instance == null) 
      { 
       DalHelper.CreateInstance<CustomerProvider>(out _instance); 
      } 
      return _instance; 
     } 
    } 
    private static CustomerProvider _instance; 

    public abstract List<CustomerEntity> GetCustomersByUserPermission(Guid userId); 

} 

Это позволило бы мне чтобы поместить конкретный код в пределах CustomerProvider.Instance.MyNonGenericMethod(), а затем получить доступ к PositionProvider, я мог бы сделать CustomerProvider.Instance.Common.GetList() ... Это действительно похоже на хак.

+0

Я просто могу сказать вам: используйте NHibernate, и он сделает все для вас. –

+0

Я бы предпочел использовать EntitySpaces на самом деле - это лучше и быстрее - но я не могу использовать его в этом проекте. – GenericTypeTea

+1

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

ответ

0

Я взломал его. Мой Наследование класс теперь стал следующим:

public abstract class CustomerProvider : PositionProvider<CustomerEntity> 
{ 

     public CustomerProvider() { } 

     public new static CustomerProvider Instance 
     { 
      get 
      { 
       if (_instance == null) 
       { 
        DalHelper.CreateInstance<CustomerProvider>(out _instance); 
       } 
       return _instance; 
      } 
     } 
     private static CustomerProvider _instance; 

     public override List<CustomerEntity> GetList() 
     { 
      return PositionProvider<CustomerEntity>.Instance.GetList(); 
     } 

     public abstract List<CustomerEntity> GetCustomersByUserPermission(Guid userId); 

} 

Он имеет конкретную реализацию следующим образом:

public class SqlCustomerProvider : CustomerProvider 
{ 
    public override List<CustomerEntity> GetCustomersByUserPermission(Guid userId) 
    { 
     using (SqlConnection cn = new SqlConnection(Globals.Instance.ConnectionString)) 
     { 
      SqlCommand cmd = new SqlCommand("GetRP_CustomersByUser", cn); 
      cmd.Parameters.Add("@UserId", SqlDbType.UniqueIdentifier).Value = userId; 
      cmd.CommandType = CommandType.StoredProcedure; 
      cn.Open(); 
      return this.GetCollectionFromReader(this.ExecuteReader(cmd)); 
     } 
    } 
} 

Мой PositionProvider остается тем же, но, вызвав его в переопределениях в расширяющейся CustomerProvider, он затем использует SqlPositionProvider для конкретного кода поставщика.

Теперь я могу достичь того, чего хотел.

// Returns a list of customers still using the PositionProvider 
CustomerProvider.Instance.GetList(); 

// Returns my specific customer data 
CustomerProvider.Instance.GetCustomersByUserPermission(); 

// Returns a list of sites still using the PositionProvider 
SiteProvider.Instance.GetList(); 

// Not part of the SiteProvider! 
SiteProvider.Instance.GetCustomersByUserPermission(); 
+0

приятно! но разве вы не устали читать/писать «постоянно»? ; P –

+0

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

1

«Правильное» место для такого метода поиска будет состоять из класса Repository. Там вы можете собрать все такие функции запросов от объектов домена.

Вот небольшой пример:

public static class Repository { 
    public static List<CustomerEntity> GetCustomersByUserPermission(
     PositionProvider<CustomerEntity> source, Guid userId) 
    { 
     // query source and return results 
    } 
} 

Добавить все «специальные» запросы к этому классу.

+0

Мои мозги уже наполовину обжарены из вышеперечисленного. У вас есть быстрый пример C# в приведенном выше контексте? – GenericTypeTea

0

Как насчет добавить что-то вроде этого, чтобы ваш абстрактный класс:

public IEnumerable<T> GetItems(Predicate<T> match) 
{ 
    foreach (T item in GetList()) 
    { 
     if (match(item)) 
      yield return item; 
    } 
} 

И тогда вы должны удалить метод SetPositionType(...), потому что это использование кажется немного неудобно (вы должны установить тип позиции, а затем позвоните по телефону GetList()?)

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

customerProvider.GetItems(customer => customer.Id == someId); 

(или, используя синтаксис .Net 2.0)

customerProvider.GetItems(delegate(Customer c) 
{ 
    return c.Id == someId; 
}); 
+0

Я не думаю, что это помогает мне. У меня есть класс (CustomerProvider), который наследуется от другого класса (PositionProvider). Я хочу использовать всю функциональность из конкретной реализации SqlPositionProvider для PositionProvider, но без дублирования кода в моем CustomerProvider. – GenericTypeTea

+0

Я не спрашиваю, как получить предметы. У меня уже все работает. Я хочу знать, как расширить свой CustomerProvider при сохранении PositionProvider, как сейчас. – GenericTypeTea

1

Если я правильно понимаю, вам нужен способ, чтобы включить некоторые метод на дочерних классах, но не во всех.

Если вы можете сгруппировать дополнительные методы, которые вам нужны, вы можете использовать интерфейс, реализовать его и использовать экземпляр этого нового класса внутри своих детей (состав).

Упрощение этого шаблона репозитория для всех ваших классов детей (в этом примере не используются интерфейсы).

[Примечание: код не может скомпилировать, только для демонстрации предлагает]

public class PositionProviderRepository 
{ 
    public List<T> GetList() 
     { 
      int positionTypeId = (int)this.PositionType; 
      using (SqlConnection cn = new SqlConnection(Globals.Instance.ConnectionString)) 
      { 
       SqlCommand cmd = new SqlCommand("Get_PositionListByPositionTypeId", cn); 
       cmd.Parameters.Add("@PositionTypeId", SqlDbType.Int).Value = positionTypeId; 
       cmd.CommandType = CommandType.StoredProcedure; 
       cn.Open(); 
       return this.GetCollectionFromReader(this.ExecuteReader(cmd)); 
      } 
     } 
    public List<CustomerEntity> GetCustomersByUserPermission(Guid userId) { 
     //TODO: implementation 
    } 
} 

И затем использовать этот класс внутри всех объектов, как CustomerEntity.

Это может эффективно заменить ваш класс SqlPositionProvider<T>, но я не уверен, что правильно понимаю вашу архитектуру, у вас очень сложная иерархия.

+0

Я думаю, что вы понимаете, что мне нужно, и это здорово ... однако, если бы мой SiteProvider и CustomerProvider реализовали этот класс, то они оба могли бы увидеть метод GetCustomersByUserPermission(), который, я, очевидно, не хотеть. Или я бы создал класс, который наследует от этого, и только предоставляет методы, которые мне нужны? – GenericTypeTea

+0

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

0

Во-первых, .NET BCL имеет хороший уровень абстракции для разных СУБД, определенных в System.Data.Common. Используя DbConnection вместо SqlConnection/OracleConnection, DbCommand вместо SqlCommand/OracleCommand и т. Д. Вы сможете немного уменьшить дублирование кода (были бы обнаружены ошибки, например, различия в именах параметров, но их можно преодолеть).

Во-вторых, IMHo - это плохая идея построить весь ваш код вокруг одиночных игр. Почему бы Вам не написать

public class CustomerProvider 
{ 
    PositionProvider<CustomerEntity> _provider; 
    PositionProvider<CustomerEntity> Instance // we don't need it public really. 
    { 
     get 
     { 
      if ((int)PositionProvider<CustomerEntity>.Instance.PositionType == 0) 
      { 
       _provider = new PositionProvider<CustomerEntity>(); // PositionType is set in .ctor 
       // we can also use a factory to abstract away DB differences 
      } 
      return _provider; 
     } 
    } 
    // one way of implementing custom query 
    public List<CustomerEntity> GetCustomersByUserPermission(Guid userId){ 
     return _provider.GetListWithCriteria(Criteria.Argument("UserId", userId)); 
    } 
} 

Метод GetListWithCriteria может быть реализован как:

public List<CustomerEntity> GetListWithCriteria(params ICriterion[] criterias){ 
     int positionTypeId = (int)this.PositionType; 
     using (DbConnection cn = OpenConnection()) // creates DbConnection and opens it 
     using (DbCommand cmd = cn.CreateCommand()) 
     { 
      // ... setting command text ... 
      foreach(ICriterion c in criterias){ 
       DbParameter p = cmd.CreateParameter(); 
       p.DbType = c.DbType; 
       p.Name = Encode(c.Name); // add '@' for MS SQL, ':' for Oracle 
       p.Value = c.Value; 
       cmd.AddParameter(p); 
      } 
      return this.GetCollectionFromReader(this.ExecuteReader(cmd)); 
     }   
} 

таким образом PositionProvider остается способом абстрагирования от различий СУБДА и CustomerProviders может построен произвольные новые запросы.

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