从复杂的数据库函数构建复杂对象的好的设计模式是什么?
What is a good design pattern to build complex objects from complex database function?
我有一个复杂的结构,需要使用从数据库中读取的数据来构建,我需要知道在哪里放置我的数据库访问逻辑。
我阅读了存储库设计模式,但我的数据库操作不是简单的 CRUD,它们不仅仅是 returning 简单的值。
我正在连接到 PostgreSQL 并调用经常 return 数据作为一组游标的函数。
这是我的代码的一个非常简化的部分...我省略了一些细节
class Topic
{
public string TopicLabel { get; set; }
public int TopicCode { get; set; }
List<Topic> parentTopics;
public Topic(int topicCode , string topicLabel)
{
...
}
}
class InitialTopic
{
Topic initialTopic;
public int TopicCode { get { return initialTopic.TopicCode; } }
Dictionary<int, float> similarityValues;
public InitialTopic( Topic topic)
{
initialTopic = topic;
similarityValues = new Dictionary<int, float>();
}
}
class TopicsDictionary
{
Dictionary<int, Topic> topics;
public TopicsDictionary()
{
topics = new Dictionary<int, Topic>();
}
public Topic this[int topicCode]
{
get
{
Topic t = null;
if (topics.ContainsKey(topicCode))
{
t = topics[topicCode];
}
else
{
t = new Topic(topicCode);
topics.Add(topicCode, t);
}
return t;
}
}
}
.
.
.
public static void GetData(InitialTopic initialTopic)
{
using (var conn = new NpgsqlConnection(connString))
{
conn.Open();
NpgsqlTransaction tran = conn.BeginTransaction();
NpgsqlCommand command = new NpgsqlCommand("public.\"GetStuff\"", conn);
.
.
string cursor1, cursor2;
using (var dr = command.ExecuteReader())
{
dr.Read();
cursor1 = dr[0].ToString();
dr.Read();
cursor2 = dr[0].ToString();
}
using (var resultSet1 = new NpgsqlCommand())
{
resultSet1.CommandText = $@"FETCH ALL FROM ""{cursor1}""";
resultSet1.Connection = conn;
using (var reader = resultSet1.ExecuteReader())
{
// here read values, create Topic objects,
// add them to TopicsDictionary and link them using parentTopics list
// to reflect parent-child relation
}
}
using (var resultSet2 = new NpgsqlCommand())
{
resultSet2.CommandText = $@"FETCH ALL FROM ""{cursor2}""";
resultSet2.Connection = conn;
using (var reader = resultSet2.ExecuteReader())
{
// here read values and fill similarityValues
// dictionary in InitialTopic object
}
}
tran.Commit();
conn.Close();
}
}
我应该将数据库操作与实际构建对象(主题对象及其成员列表和字典)分开吗?我该怎么做?这种情况有合适的设计模式吗?
Should I separate database operation from the actual building of objects (topic objects and their member lists and dictionaries) ?
当然可以。 using (var reader =
中的两个代码块足够复杂,以至于 (1) 您的 GetData
方法看起来很难看,(2) 您想要对这些代码进行单元测试,以及 (3) 您想要重用它们切换到另一个数据库系统时的代码,例如 MySQL.
How should I do that? Is there an appropriate design pattern for such case?
只需将 GetData
方法中的两个代码块提取到其他地方即可。由于您的 InitialTopic
class 非常干净,您可以移至此处。但这取决于你。
现在唯一的挑战是InitialTopic
class如何从两个阅读器接收数据。当然,我们会将读者传递给 initialTopic
对象。但是我们不应该让 InitialTopic
class 依赖于数据库 class (你的读者的类型)。
通常我们应用依赖倒置原则来解决这个问题。引入了一个新的接口来抽象读者的操作:
interface MyReader {
// hasNext() and next() methods, right?
}
我们会让 InitialTopic
class 依赖于 MyReader
接口。
现在我们写一个适配器来适配Npgsql阅读器(using (var reader =
中的阅读器):
class MyNpgsqlReader : MyReader {
// use Dependency Injection to inject the Npgsql reader
}
最后,GetData
方法中的两个代码块变成:
using (var reader = resultSet1.ExecuteReader()) {
initialTopic.method1(new MyNpgsqlReader(reader));
}
...
using (var reader = resultSet2.ExecuteReader()) {
initialTopic.method2(new MyNpgsqlReader(reader));
}
我有一个复杂的结构,需要使用从数据库中读取的数据来构建,我需要知道在哪里放置我的数据库访问逻辑。
我阅读了存储库设计模式,但我的数据库操作不是简单的 CRUD,它们不仅仅是 returning 简单的值。
我正在连接到 PostgreSQL 并调用经常 return 数据作为一组游标的函数。
这是我的代码的一个非常简化的部分...我省略了一些细节
class Topic
{
public string TopicLabel { get; set; }
public int TopicCode { get; set; }
List<Topic> parentTopics;
public Topic(int topicCode , string topicLabel)
{
...
}
}
class InitialTopic
{
Topic initialTopic;
public int TopicCode { get { return initialTopic.TopicCode; } }
Dictionary<int, float> similarityValues;
public InitialTopic( Topic topic)
{
initialTopic = topic;
similarityValues = new Dictionary<int, float>();
}
}
class TopicsDictionary
{
Dictionary<int, Topic> topics;
public TopicsDictionary()
{
topics = new Dictionary<int, Topic>();
}
public Topic this[int topicCode]
{
get
{
Topic t = null;
if (topics.ContainsKey(topicCode))
{
t = topics[topicCode];
}
else
{
t = new Topic(topicCode);
topics.Add(topicCode, t);
}
return t;
}
}
}
.
.
.
public static void GetData(InitialTopic initialTopic)
{
using (var conn = new NpgsqlConnection(connString))
{
conn.Open();
NpgsqlTransaction tran = conn.BeginTransaction();
NpgsqlCommand command = new NpgsqlCommand("public.\"GetStuff\"", conn);
.
.
string cursor1, cursor2;
using (var dr = command.ExecuteReader())
{
dr.Read();
cursor1 = dr[0].ToString();
dr.Read();
cursor2 = dr[0].ToString();
}
using (var resultSet1 = new NpgsqlCommand())
{
resultSet1.CommandText = $@"FETCH ALL FROM ""{cursor1}""";
resultSet1.Connection = conn;
using (var reader = resultSet1.ExecuteReader())
{
// here read values, create Topic objects,
// add them to TopicsDictionary and link them using parentTopics list
// to reflect parent-child relation
}
}
using (var resultSet2 = new NpgsqlCommand())
{
resultSet2.CommandText = $@"FETCH ALL FROM ""{cursor2}""";
resultSet2.Connection = conn;
using (var reader = resultSet2.ExecuteReader())
{
// here read values and fill similarityValues
// dictionary in InitialTopic object
}
}
tran.Commit();
conn.Close();
}
}
我应该将数据库操作与实际构建对象(主题对象及其成员列表和字典)分开吗?我该怎么做?这种情况有合适的设计模式吗?
Should I separate database operation from the actual building of objects (topic objects and their member lists and dictionaries) ?
当然可以。 using (var reader =
中的两个代码块足够复杂,以至于 (1) 您的 GetData
方法看起来很难看,(2) 您想要对这些代码进行单元测试,以及 (3) 您想要重用它们切换到另一个数据库系统时的代码,例如 MySQL.
How should I do that? Is there an appropriate design pattern for such case?
只需将 GetData
方法中的两个代码块提取到其他地方即可。由于您的 InitialTopic
class 非常干净,您可以移至此处。但这取决于你。
现在唯一的挑战是InitialTopic
class如何从两个阅读器接收数据。当然,我们会将读者传递给 initialTopic
对象。但是我们不应该让 InitialTopic
class 依赖于数据库 class (你的读者的类型)。
通常我们应用依赖倒置原则来解决这个问题。引入了一个新的接口来抽象读者的操作:
interface MyReader {
// hasNext() and next() methods, right?
}
我们会让 InitialTopic
class 依赖于 MyReader
接口。
现在我们写一个适配器来适配Npgsql阅读器(using (var reader =
中的阅读器):
class MyNpgsqlReader : MyReader {
// use Dependency Injection to inject the Npgsql reader
}
最后,GetData
方法中的两个代码块变成:
using (var reader = resultSet1.ExecuteReader()) {
initialTopic.method1(new MyNpgsqlReader(reader));
}
...
using (var reader = resultSet2.ExecuteReader()) {
initialTopic.method2(new MyNpgsqlReader(reader));
}