SQL记录(实体)中表达关系数量限制
有一个使用数据库持久性的应用程序(实体框架,但不确定是否重要)
给定以下假设布局:
其中所有这些对象的一部分派生自 AbstractBase。 Container 是一个对象,充当任意数量的 AbstractBase 派生对象的集合。
问题
我想创建一个限制子系统,它允许我们定义容器中可以包含的各个 AbstractBase 项的数量。
例如,容器可以有零个容器,可以有零个或一个对象,必须恰好有一个 AnotherObject,可以有许多 AbstractObject,等等。
简单的方法
AbstractBase 中名为 CountRestrictor 的字段是一个小 int。这对应于数据库外部持有属性的枚举。问题:数据库中不包含该内容。对数据库的更改需要更改该程序集的枚举容器(从而重建)。另外,我必须在其他地方编写数学翻译代码。
基于类的方式
那么,类呢?问题是数据库中的类需要数据类型,那么我们可以将这种数学限制表达为数据类型吗?例如,我可以创建一个包含 lambda 表达式的一部分的类,该表达式稍后可以转换为表达式项吗?我不这么认为。
我考虑过的事情
嵌入式数学逻辑
也许是一个具有字符串类型的 CountObject.Restrictor 属性的 CountObject,可以以编程方式将其转换为 Expression 对象:
CountObject lessThanTwo = new CountObject { Restrictor = "< 2" };
CountObject exactlyOne = new CountObject { Restrictor = "= 1" };
在 Container 对象内部时,我可以有类似的逻辑:
…
private Bool IsValidEntry<T>(T obj) where T : AbstractBase
{
Int count = this.AbstractBases.OfType<T>().Count;
Expression expression = new Expression(); // No constructors defined, so not sure how
// use obj.Restrictor to build the expression
if (expression)
// Add element
else
// throw Exception/Message dialog
…
}
这可能吗?是否可取(因为我将数学注入到数据库中,不过,不是很多)
手动字符串到数学翻译
我考虑的另一件事是仅使用 CountObject.Restrictor 作为人类可读的字符串“少于两个”,“恰好一个”等等,并且在数据库之外有另一个对象进行翻译:
public class CountTranslator
{
private String _lessThanTwo = "Less than Two";
private String _exactlyOne = "Exactly One";
public String LessThanTwo { get { return _lessThanTwo; } }
…
}
这将完全允许使用 Module.CountTranslator.LessThanTwo,但不会存储在数据库中,需要重建更改。它对拼写错误很敏感(“小于二”!=“小于二”),并且仍然需要构建“人类到数学”的代码:
…
Int count = container.AbstractBase.OfType<T>();
Int restrictor = obj.CountObject.Restrictor;
switch(restrictor)
{
case CountTranslator.ExactlyOne // Have to make sure database record string spelled correctly
if (count != 1)
// do something
…
}
但这让我觉得非常难看,需要进行大量的条件检查。
加性条件
最后,我考虑了加性条件。 AbstractBase 与 CountObject 具有多对多关系。
public class CountObject
{
private Int _value;
private String _expression;
public Int Value { get { return _value; } }
public String Expression { get { return _Expression; } }
}
public partial class Container : AbstractBase
{
…
private Bool IsValidEntry<T>(T obj) where T : AbstractBase
{
Int count = AbstractBases.OfType<T>().Count;
foreach (CountObject counter in obj.CountObjects)
{
switch(counter.Expression)
{
case "<":
if (count > counter.Value)
throw Exception;
case "=":
if (count != counter.Value)
throw Exception;
…
}
}
}
}
同样,这是很多条件语句和 switch 语句。
尾声
还有其他方法可以给猫剥皮吗?也许“数学翻译类”隐藏在.NET中的某个地方?有没有一种方法可以体现最佳实践?
Have an application using database persistence (entity framework, but not sure that matters)
Given the following hypothetical layout:
Where all of these objects derive from the AbstractBase. Container is an object that acts as a collection for an arbitrary number of AbstractBase-derived objects.
Problem
I want to create a restriction subsystem that will allow us to define the quantity of individual AbstractBase items that can be in a Container.
For instance, Container can have zero Containers, can have zero or one Objects, must have exactly one AnotherObject, can have many AbstractObjects, etc.
Simple way
A field in AbstractBase called CountRestrictor that's a small int. This corresponds to an enum outside of the database holding an attribute. Problem: This is not contained in the database. A change to the database requires a change in that enum container (and thus a rebuild) of that assembly. Plus, I have to write math translation code elsewhere.
Class-based way
So, what about a class? The problem is that classes in the database require datatypes, so can we express this mathematical restriction as a datatype? Can I make a class that holds part of a lambda expression that can be later translated into an Expression item, for instance? I don't think so.
Things I've Considered
Embedded mathematical logic
Maybe a CountObject with a CountObject.Restrictor attribute of type string that could be programmatically translated into an Expression object:
CountObject lessThanTwo = new CountObject { Restrictor = "< 2" };
CountObject exactlyOne = new CountObject { Restrictor = "= 1" };
While inside the Container object I can have logic something like:
…
private Bool IsValidEntry<T>(T obj) where T : AbstractBase
{
Int count = this.AbstractBases.OfType<T>().Count;
Expression expression = new Expression(); // No constructors defined, so not sure how
// use obj.Restrictor to build the expression
if (expression)
// Add element
else
// throw Exception/Message dialog
…
}
Is this possible? Is it advisable (since I'm injecting math into my database, though, not a lot)
Manual string to math translation
Another thing I considered is just using CountObject.Restrictor as a human readable string "Less that Two", "Exactly One", etc. and having another object outside the database that does translation:
public class CountTranslator
{
private String _lessThanTwo = "Less than Two";
private String _exactlyOne = "Exactly One";
public String LessThanTwo { get { return _lessThanTwo; } }
…
}
This would cleanly allow the use of Module.CountTranslator.LessThanTwo, but wouldn't be stored in the database, requiring a rebuild for changes. It would be sensitive to misspelling ("Less Than Two" != "Less than Two"), and would still require the building of "human to math" code:
…
Int count = container.AbstractBase.OfType<T>();
Int restrictor = obj.CountObject.Restrictor;
switch(restrictor)
{
case CountTranslator.ExactlyOne // Have to make sure database record string spelled correctly
if (count != 1)
// do something
…
}
But this strikes me as horribly ugly with a lot of conditional checking.
Additive conditions
Finally, I've considered additive conditions. AbstractBase has a many-to-many relationship with CountObject.
public class CountObject
{
private Int _value;
private String _expression;
public Int Value { get { return _value; } }
public String Expression { get { return _Expression; } }
}
public partial class Container : AbstractBase
{
…
private Bool IsValidEntry<T>(T obj) where T : AbstractBase
{
Int count = AbstractBases.OfType<T>().Count;
foreach (CountObject counter in obj.CountObjects)
{
switch(counter.Expression)
{
case "<":
if (count > counter.Value)
throw Exception;
case "=":
if (count != counter.Value)
throw Exception;
…
}
}
}
}
Again, this is a lot of conditionals and switch statements.
Coda
Are there other ways to skin the cat? Perhaps a "Mathematical translation class" hidden in .NET somewhere? Is there one way that exemplifies Best Practices?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您能否确定哪一个是“关系”、“数据库”或“SQL”,我真的可以在这些领域为您提供帮助。
很棒的类图。你有数据模型吗?
RDBMS 拥有并管理自己的数据缓存。所以我的第三个问题是,你为什么要写一个?
Could you please identify which bit is "relational" or "database" or "SQL", I can really help you in those areas.
Great class diagram. Do you have a Data Model ?
RDBMS have, and manage, their own data caches. So my third question is, why are you writing one ?
我不是数据库专家。因此,我将所有业务逻辑保留在代码中,仅使用数据库来存储数据。话虽如此,我真的会在不涉及 db 的情况下解决你的问题。您的容器可能具有定义包含规则的类型或类似类型(或者它可能是可插入策略类)。添加元素时会强制执行该规则。
I am not a db expert. As such I keep all my business logic in the code using the db just for storing data. Having said that I'd really tackle your problem without involving db. Your container may be attributed with a type or similar that will define the containment rules (or it may be pluggable policy class). The rule is enforced when an element is being added.