2014-10-20 180 views
3

我有一个数据库,其中包含产品。 这些产品被分类,有类别子类别将枚举映射到“子枚举”

例如:

Product p1 = new Product() 
{ 
    Category = Category.Fruit, 
    Subcategory = Subcategory.Apple 
}; 

我的问题是,我要限制的子类别,根据不同的类别的事实。

下面的例子不应该是可能的:

Product p2 = new Product() 
{ 
    Category = Category.Fruit, 
    Subcategory = Subcategory.Cheese 
}; 

此外我想能够返回一个字符串数组(每个类别匹配的枚举),其各自具有相应的子类别的阵列。

我一直在想一段时间,但一无所获,也没有找到任何解决方案。

会有什么建议?

+1

这里没有编译时的方式,除非你使用的不同的子类别组独立的枚举做到这一点。有很多不同的方法可以在运行时进行验证。 – 2014-10-20 18:04:51

+6

继承层次结构可能更适合您的需求 – 2014-10-20 18:06:17

+0

这是业务逻辑,而不是纯粹的技术解决方案。基本上,类别和子类别必须在某处(数据库/代码)进行定义,并且必须对其进行查找。 – 2014-10-20 18:06:37

回答

3

我喜欢地图规则。 你也可以在你的枚举值上加一个自定义属性。

例如:

public enum Subcategory { 
    [SubcategoryOf(Category.Fruit)] 
    Apple, 
    [SubcategoryOf(Category.Dairy)] 
    Emmenthaler 
} 

这需要编写一个SubcategoryOfAttribute类(参见here用于MS指南)。然后你可以编写一个验证器,它可以查看任何子类别并从中获取合法的父类别。

在地图上的优势在于该关系在声明中很好地阐述了出来。

缺点是每个子类别最多可以有一个父类别。

我觉得这很有趣,所以我把它取出来了。首先,属性:

[AttributeUsage(AttributeTargets.Field)] 
public class SubcategoryOf : Attribute { 
    public SubcategoryOf(Category cat) { 
     Category = cat; 
    } 
    public Category Category { get; private set; } 
} 

然后我们做一些模拟枚举

public enum Category { 
    Fruit, 
    Dairy, 
    Vegetable, 
    Electronics 
} 

public enum Subcategory { 
    [SubcategoryOf(Category.Fruit)] 
    Apple, 
    [SubcategoryOf(Category.Dairy)] 
    Buttermilk, 
    [SubcategoryOf(Category.Dairy)] 
    Emmenthaler, 
    [SubcategoryOf(Category.Fruit)] 
    Orange, 
    [SubcategoryOf(Category.Electronics)] 
    Mp3Player 
} 

现在我们需要一个断言,以确定是否一个子类别匹配的类别(注:可以有多个父类,如果你想要 - 你需要修改属性和这个谓词来获得所有属性并检查每一个属性。

public static class Extensions { 
    public static bool IsSubcategoryOf(this Subcategory sub, Category cat) { 
     Type t = typeof(Subcategory); 
     MemberInfo mi = t.GetMember(sub.ToString()).FirstOrDefault(m => m.GetCustomAttribute(typeof(SubcategoryOf)) != null); 
     if (mi == null) throw new ArgumentException("Subcategory " + sub + " has no category."); 
     SubcategoryOf subAttr = (SubcategoryOf)mi.GetCustomAttribute(typeof(SubcategoryOf)); 
     return subAttr.Category == cat; 
    } 
} 

然后你把你的产品类型来测试它:

public class Product { 
    public Product(Category cat, Subcategory sub) { 
     if (!sub.IsSubcategoryOf(cat)) throw new ArgumentException(
      String.Format("{0} is not a sub category of {1}.", sub, cat), "sub"); 
     Category = cat; 
     Subcategory = sub; 
    } 

    public Category Category { get; private set; } 
    public Subcategory Subcategory { get; private set; } 
} 

测试代码:

Product p = new Product(Category.Electronics, Subcategory.Mp3Player); // succeeds 
Product q = new Product(Category.Dairy, Subcategory.Apple); // throws an exception 
+0

非常感谢 - 这是我最终使用的是什么。很棒! – JensOlsen112 2014-10-22 16:38:02

2

我的建议是有一个Dictionary<SubCategory, Category>,它将您的SubCategory映射到您的Category

之后,你可以摆脱Category对您的产品一起,或者你可以使用一个辅助方法

public class Product 
{ 
    static Dictionary<SubCategory, Category> _categoriesMap; 

    public static Product() 
    { 
     _categoriesMap = new Dictionary<SubCategory, Category>(); 
     _categoriesMap.Add(SubCategory.Apple, Category.Fruit); 
    } 

    public SubCategory SubCategory { get; set; } 

    public Category Category 
    { 
     get { return _categoriesMap[this.SubCategory]; } 
    } 
} 
3

你所要做的就是代表一阶逻辑(http://en.wikipedia.org/wiki/First-order_logic )使用枚举。并与数据库同步。在代码中对它进行硬编码时,这不是一件容易的事。已经提出了许多很好的解决方案。

就我而言,我只是使用字符串(或唯一标识符)的类别和子类别,并强制使用数据库中定义的规则的完整性。但是如果你最终在代码中使用它,它不会是编译时。

Enum的问题是它必须与你的外部源和你的代码相匹配。此外,它很难附加更多的信息,如价格或国家,或者即使你有不同种类的苹果。