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

发布于 2024-08-09 11:13:10 字数 3904 浏览 3 评论 0原文

我的数据访问层给自己带来了一些问题。在这个特定的实例中,我有一个表,其中可能包含 5 种类型的“实体”。这些基本上是公司、客户、站点等。类型由表中的 PositionTypeId 指定。它们都在同一个表中,因为它们都具有相同的数据结构; 位置 ID、描述和代码

我有一个主要抽象类,如下所示:

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 的继承类中。这是我的 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 等Provider 的公共属性中:

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()... 这看起来确实像不过有点黑客行为。

I've caused myself a bit of an issue with my Data Access Layer. In this particular instance, I have a table that contains potentially 5 types of 'entity'. These are basically Company, Customer, Site, etc. The type is dictated by a PositionTypeId within the table. They're all in the same table as they all havethe same data structure; PositionId, Description and Code.

I have a main abstract class as follows:

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;
    }

}

I want to then be able to put all the general code within an inherting class that is either SQL or Oracle based. This is my SQL implementation:

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));
            }
        }
}

I've then create a class for each type as follows (this is the CustomerProvider as an example):

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;
        }
    }
}

This all works fantastically... until I realised that I have certain functions that are related specifically to certain position types. I.e. I need to be able to get all Customers (which is an IPositionType) based on the user permissions.

So I need to add another abstract method:

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

Now, obviously I don't want this within my PositionProvider abstract class as that would mean that method would appear when dealing with the site/company provider.

How can I add this, and other, additional methods without having to duplicate the code within the SqlPositionProvider?

Edit:

The only idea I've come up with is to separate the PositionProvider out into a common property of the 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);

}

This would allow me to put the specific code within CustomerProvider.Instance.MyNonGenericMethod(), and then to access the PositionProvider I could do CustomerProvider.Instance.Common.GetList()... This does seem like a bit of a hack though.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

恋竹姑娘 2024-08-16 11:13:10

如果我理解正确的话,你需要一种方法来在子类中包含一些方法,但不是全部。

如果您可以对所需的额外方法进行分组,您可以使用一个接口,实现它,并在您的子级(组合)中使用这个新类的实例。

对此的简化是所有子类的存储库模式(此示例不使用接口)。

[注意:代码无法编译,仅用于演示建议]

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 但我不确定我是否正确理解您的架构,您有一个非常复杂的层次结构。

If I understand correctly you need a way to include some method on child classes, but not in all.

If you can group the extra methods you need, you could use an interface, implement it, and use a instance of this new class inside your children (composition).

A simplification of this is a repository pattern for all your children classes (this example does not uses interfaces).

[NOTE: code could not compile, just for demonstration proposes]

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
    }
}

And then you use this class inside the all the entities like CustomerEntity.

This could effectively replace your class SqlPositionProvider<T> but I'm not sure I understand correctly your architecture, you have a very complex hierarchy.

情何以堪。 2024-08-16 11:13:10

这种查找方法的“正确”位置是 Repository 类。在那里,您可以从域对象中收集所有此类查询函数。

这是一个小例子:

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

将所有“特殊”查询添加到此类中。

The "proper" place for such a lookup method would be a Repository class. There you can collect all such query functions away from your domain objects.

Here's a small example:

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

Add all your "special" queries to this class.

半岛未凉 2024-08-16 11:13:10

如何将这样的内容添加到您的抽象类中:

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;
});

How about adding something like this to your abstract class:

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

And then you should remove the SetPositionType(...) method because it's usage seems a bit awkward (you are supposed to set a position type and then call GetList()?)

So, using the method you could simply write:

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

(or, using the .Net 2.0 syntax)

customerProvider.GetItems(delegate(Customer c)
{
     return c.Id == someId;
});
何其悲哀 2024-08-16 11:13:10

首先,.NET BCL 对于 System.Data.Common 中定义的不同 DBMS 有一个很好的抽象级别。通过使用 DbConnection 而不是 SqlConnection/OracleConnectionDbCommand 而不是 SqlCommand/< code>OracleCommand 等,您将能够稍微减少代码重复(会有一些陷阱,例如参数命名的差异,但它们是可以克服的)。

其次,恕我直言,围绕单例构建所有代码是一个坏主意。为什么不编写

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 可以构建任意新查询。

First, .NET BCL has a nice abstraction level for different DBMSes defined in System.Data.Common. By using DbConnection instead of SqlConnection/OracleConnection, DbCommand instead of SqlCommand/OracleCommand etc you'll be able to reduce code duplication a bit (there would be gotchas, such as differencies in parameter naming but they can be overcome).

Second, IMHo it is a bad idea to build all your code around singletons. Why wouldn't you write

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));
    }
}

Method GetListWithCriteria can be implemented as:

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));
        }        
}

In such a way PositionProvider remains a way of abstracting away DBMS differences and CustomerProviders can built arbitrary new queries.

葬﹪忆之殇 2024-08-16 11:13:10

我已经破解了。我的 Inheriting 类现已变为以下内容:

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(); 

I've cracked it. My Inheriting class has now become the following:

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);

}

It has a concrete implementation as follows:

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));
        }
    }
}

My PositionProvider remains the same, but by calling it in the overrides in the extending CustomerProvider, it then uses the SqlPositionProvider for the provider specific code.

I can now achieve what I wanted.

// 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(); 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文