开发者

Expressing relationship quantity restrictions in a SQL record (Entity)

开发者 https://www.devze.com 2023-02-04 11:59 出处:网络
Have an application using database persistence (entity framework, but not sure that matters) Given the following hypothetical layout:

Have an application using database persistence (entity framework, but not sure that matters)

Given the following hypothetical layout:

Expressing relationship quantity restrictions in a SQL record (Entity)

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开发者_JS百科.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?


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 ?


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号