需要一些帮助来解决我的DAL中的主要抽象模式问题

时间:2009-11-02 14:13:58

标签: c# .net generics design-patterns abstract-class

我的数据访问层引起了一些问题。在这个特定的例子中,我有一个包含潜在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() ...这看起来有点像黑客

5 个答案:

答案 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 / OracleConnectionDbCommand代替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();