我的数据访问层引起了一些问题。在这个特定的例子中,我有一个包含潜在5种类型的“实体”的表。这些基本上是公司,客户,网站等。类型由表中的PositionTypeId决定。它们都在同一个表中,因为它们都具有相同的数据结构; PositionId,说明和代码。
我有一个主要的抽象类如下:
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;
}
}
我希望能够将所有通用代码放在基于SQL或Oracle的inherting类中。这是我的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中的代码的情况下添加此方法和其他其他方法?
修改
我想出的唯一想法是将PositionProvider分离为CustomerProvider,SiteProvider等提供的公共属性:
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 :(得分:1)
这种查找方法的“正确”位置是Repository类。在那里,您可以从域对象中收集所有此类查询功能。
这是一个小例子:
public static class Repository {
public static List<CustomerEntity> GetCustomersByUserPermission(
PositionProvider<CustomerEntity> source, Guid userId)
{
// query source and return results
}
}
将所有“特殊”查询添加到此课程中。
答案 1 :(得分: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>
,但我不确定我是否正确理解您的架构,您的层次结构非常复杂。
答案 2 :(得分: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;
});
答案 3 :(得分:0)
首先,.NET BCL对System.Data.Common
中定义的不同DBMS有一个很好的抽象级别。使用DbConnection
代替SqlConnection
/ OracleConnection
,DbCommand
代替SqlCommand
/ OracleCommand
等,您将能够减少代码重复bit(会有问题,例如参数命名中的差异,但可以克服它们。)
其次,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仍然是一种抽象DBMS差异的方式,CustomerProviders
可以构建任意新的查询。
答案 4 :(得分: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();