MVC高效的动态EF数据库调用

时间:2013-07-24 18:32:44

标签: c# asp.net-mvc entity-framework

我正在开发一个MVC应用程序。很多视图都是搜索引擎,允许用户选择参数来获取他想要的数据。

我正在寻找一种有效的方式来对数据库进行动态调用,这样我就可以只检索想要的数据,而不是取出一堆数据并将它们排序。

到目前为止,我一直在使用Dynamic Linq,但是我遇到了很多麻烦,我想知道是否有更好的,更少的麻烦。唯一的因素是我知道我正在查看的表格的字段,我需要能够使用><=等运算符。

修改

以下是基于我的应用程序的表格示例:

TABLE CARD
(
    CARD_IDE INT NOT NULL IDENTITY,
    CARD_NAME VARCHAR(50) NOT NULL,
    CARD_NUMBER NUMERIC(4) NOT NULL,
    CARD_COLOR VARCHAR(10),
    CARD_MANA_COST VARCHAR(30),
    CARD_MANA_CONVT VARCHAR(3),
    CARD_TYPE VARCHAR(50),
    CARD_POWER VARCHAR(2),
    CARD_TOUGH VARCHAR(2),
    CARD_RARTY VARCHAR(1) NOT NULL,
    CARD_TEXT_ABILT VARCHAR(800),
    CARD_TEXT_FLAVR VARCHAR(800),
    CARD_ARTST_NAME VARCHAR(100),
    CARD_SET_IDE INT NOT NULL,
    CARD_FLAG_FACE INT NOT NULL DEFAULT 0,
    CARD_CHILD_IDE INT,
    CARD_MASTER_IDE INT,
    CARD_UNIT_COST NUMERIC(5,2) NOT NULL DEFAULT 0
)

以及一些例子:

  1. 用户查找"Creature" (String),编号为3且卡集IDE为6的任何项目;
  2. 任何包含单词Rat;
  3. 的卡片
  4. color BlueWhite的所有卡片,单位成本为higher than 3.00
  5. power小于3但高于1的任何卡片。
  6. 编辑2

    经过大量研究(感谢下面的Chris Pratt),我已经设法看了一下并深入研究了一些事情。

    基于this

    首先,我创建我的对象上下文:

    var objectContext = ((IObjectContextAdapter) mDb).ObjectContext;
    

    然后我创建了ObjectSet:

    ObjectSet<CARD> priceList = objectContext.CreateObjectSet<CARD>();
    

    然后我检查用户是否选择了任何值:

    if (keyValuePair.Key == CARDNAME)
    {
        queryToLoad = TextBuilder.BuildQueryStringForTextboxValue(keyValuePair.Value);
    
        //valuesToUse.Add("CARD_NAME.Contains(\"" + queryToLoad + "\")");
    
        priceList = priceList.Where(_item => _item.CARD_NAME.Contains(queryToLoad)) as ObjectSet<PRICE_LIST>;
    }
    

    其中queryToLoad实际上是要查找的值。示例:如果我的用户搜索Angel,则queryToLoad将为“Angel”。我试图在不重写整个代码的情况下得到结果。

    然后我将结果收集到这样的List中:

    listToReturn.AddRange(priceList.ToList());
    

    HOWEVER :使用此方法时遇到问题。当priceList = priceList.Where(_item => _item.CARD_NAME.Contains(queryToLoad)) as ObjectSet<PRICE_LIST>;喜欢被攻击时,值总是为null,我不知道为什么。

1 个答案:

答案 0 :(得分:1)

没有办法优化本质上具有动态性的东西。您可以在应用程序中执行的唯一操作是将最终用户选择的任何过滤器提供给Where子句,并让Entity Framework以您认为合适的最有效方式从数据库中获取结果。

但是,如果存在某些已知的约束,您可以做一些事情。至少,如果您知道将搜索哪些字段,则可以向数据库添加适当的索引,以便优化对这些特定字段的搜索。但是,您可以达到过度优化的程度(如果您为每个字段编制索引,那么您可能也没有索引)。通常最好监视数据库处理的查询,并根据实际用户行为为最常用的字段添加索引。

您还可以调查使用存储过程进行查询。根据所应用的过滤器的复杂性和数量,创建用于处理查询的存储过程可能很困难,但如果您可以将逻辑压缩到足以使其成为可能,则使用存储过程将高度优化查询。

<强>更新

这就是构建查询的意思。我将采用上面的第一个用例场景。您有三个过滤器:类型,编号和IDE。用户可以指定任何一个或两个,全部三个或全部。 (我们假设cardType是一个字符串,cardNumbercardIde是可以为空的。

var cards = db.Cards;

if (!string.IsNullOrEmpty(cardType))
{
    cards = cards.Where(m => m.Type == cardType);
}

if (cardNumber.HasValue)
{
    cards = cards.Where(m => m.Number == cardNumber);
}

if (cardIde.HasValue)
{
    cards = cards.Where(m => m.IDE == cardIde);
}

return View(cards);

实体框架实际上不会发出查询,直到您执行需要查询数据的内容(迭代列表,计算项目等)。在此之前,您对DbSet所做的任何其他操作只会添加到EF最终发送的查询中。因此,您可以通过在最终使用最终结果之前有条件地处理每个可能的过滤器来构建查询。

更新#2

抱歉,我上次更新时显然还没有喝足够的咖啡。那里显然存在类型问题。如果您将db.Cards存储到cards,则它将是DbSet<Card>类型,而对Where的任何调用的结果都是IQueryable<Card>类型,这很明显不能存储在同一个变量中。

所以你只需要将变量初始化为适合两者的东西:IEnumerable<Card>

IEnumerable<Card> cards = db.Cards;

然后你不应该得到任何类型错误。