我正在使用Web API构建API,我将数据库表作为JSON返回。
这是我的控制器方法:
public List<ExampleDataDTO> ExampleData(int offset = 0, int limit = 0)
{
return DataAccessLayer.GetExampleData(offset, limit);
}
DataAccess方法:
public List<ExampleDataDTO> GetExampleData(int offset, int limit)
{
using (var db = new Context())
{
db.Configuration.ProxyCreationEnabled = false;
var exampleQuery = db.example.AsEnumerable().Select(item =>
new ExampleDataDTO
{
//Selects
});
if(offset == 0 && limit == 0)
{
return exampleQuery .ToList();
}
else
{
return exampleQuery .Skip(offset).Take(limit).ToList();
}
}
}
现在,ExampleDataDTO包含100多个文件,我希望能够使用API过滤任何字段上的数据。
有没有办法制作动态查询参数?例如,如果我去localhost/api/ExampleData?offset=0&limit=10&name=test1&size=test2
我想获得名称和大小的键和值,以便能够将它们包含在我的LINQ代码中。
这可行吗?
编辑:
我可以将所有参数都设为IEnumerable<KeyValuePair<String, String>> queryString
有没有办法在lambda表达式中使用Key我循环遍历所有keyValuePairs?喜欢Where(c = c.Key == Value),
foreach(var queryKeyValuePair in queryString)
{
Something including Where(c => c.queryKeyValuePair.Key == queryKeyValuePair.Value)
}
答案 0 :(得分:2)
如果我理解你的问题,你可能想要做这样的事情:
public List<ExampleDataDTO> GetExampleData(int offset = 0, int limit = 0, string name = "", string size = "")
{
using (var db = new Context())
{
db.Configuration.ProxyCreationEnabled = false;
var exampleQuery = db.example
.Where((x => x.Name == name || name == "") && // If parameter 'name' has a value, filter on that, else ignore it.
(x => x.Size == size || size == "")) // If parameter 'size' has a value, filter on that, else ignore it.
.AsEnumerable()
.Select(item =>
new ExampleDataDTO
{
//Selects
});
if (offset == 0 && limit == 0)
{
return exampleQuery.ToList();
}
else
{
return exampleQuery.Skip(offset).Take(limit).ToList();
}
}
}
使方法的参数可选,并在LINQ查询中使用它们进行过滤。
答案 1 :(得分:2)
您可以使用OData与WebAPI控制器动态应用:filter,orderby,select,skip,top(take)。
例如:
// your API action
public IQueryable<ExampleDataDTO> Get(ODataQueryOptions opts)
{
// here is some odata settings to validate query params.
var settings = new ODataValidationSettings()
{
// Initialize settings as needed.
AllowedFunctions = AllowedFunctions.AllMathFunctions
};
// validating parameters
opts.Validate(settings);
var yourExampleQuery = // some data from db
// apply all parameters that came within query to your data
IQueryable results = opts.ApplyTo(yourExampleQuery.AsQueryable());
// and return this
return results as IQueryable<Product>;
}
您的查询可能类似于:http://localhost/ExampleData?$filter=SomeProperty eq 'SomeValue' $orderby=anotherProperty & $skip=10 & $top=10
所有查询参数都将解析为ODataQueryOptions
并应用于您的API调用结果。
希望它会有所帮助。
答案 2 :(得分:0)
我知道该问题的答案为时已晚,但还是想分享我的回答。
我想提出一种不同的方法,那就是将逻辑转移到DB。通过这样做,您的代码将更适应更改,并且只需要在一个地方(DB)进行更改。毕竟,查询中的更改不应要求您重新编译代码。因此,这里有一个示例,说明如何实现此方法:
型号:
public class ExampleParams
{
public int size { get; set; } = 40; // Could be any number
public int page { get; set; } = 1; // you can apply offset here but this should be done on db, see below
public string q { get; set; } = string.Empty;
}
控制器:
public List<ExampleDataDTO> ExampleData([FromQuery] ExampleParams params)
{
return DataAccessLayer.GetExampleData(params);
}
这将使您拥有动态查询参数,例如:
localhost/api/ExampleData?q=someString
将按参数q
localhost/api/ExampleData?q=someString&page=2
将显示第2页的搜索参数q
localhost/api/ExampleData?q=someString&page=2&size=10
将在第2页上显示10个搜索参数q
我假设您使用的是MySQL,但如果没有使用,则概念将保持不变。偏移量和限制可能对总体数据量不好,因此您尝试实现的分页方法可以用两种不同的方式解决。
如果您知道表不会变得非常大,那么第一个将是很好的,因此可以通过上述模型中的属性“ page”轻松有效地应用offset和limit,并且limit和offset将为动态且随时随地进行计算。
-- page and size is what you are passing via model
SET @page := IFNULL(`page`, 1);
SET @size := SELECT CASE WHEN IFNULL(`size`, 40) > 40 THEN 40 ELSE IFNULL(`size`, 40) END); -- 40 is some default max limit
SELECT * FROM elementTable LIMIT @size OFFSET ((@page - 1) * @size)
如果您知道表随着时间的推移而变得很大,那么第二种方法将是更好的选择,在这种情况下,您将实现更有效的“ timestamp_id”。有关更多参考,请单击here:
-- Given that T is the timestamp and I is the id contained in the token.
SELECT * FROM elementTable
WHERE (
timestampColumn > T
OR (timestampColumn = T AND idColumn > I)
)
AND timestampColumn < now()
ORDER BY timestampColumn asc, idColumn asc;
-- The ids in the idColumn must be unique (out-of-the-box for primary keys)
-- We need an index on both columns timestampColumn and idColumn
现在,当涉及到按字段过滤时,您几乎没有选择,但是使用动态SQL进行缩放会更好,尽管编写起来很麻烦。动态SQL将为Query参数的每个排列创建并缓存一个不同的计划,但是至少每个计划都将“针对”特定的查询(无论是PROC还是Adhoc SQL,只要它们是参数化查询,它们将被缓存)
SET @SQL = 'SELECT * FROM elementTable WHERE 1 = 1'
IF (`optionalParam1` IS NOT NULL) then
SET @SQL = CONCAT(@SQL , ' AND myColumn1 = \'', `optionalParam1`, '\' ' );
IF (`optionalParam2` IS NOT NULL) then
SET @SQL = CONCAT(@SQL , ' AND myColumn2 = \'', `optionalParam1`, '\' ' );
1 == 1
骇客攻击。如果根本没有谓词,那么WHERE也将消失。希望这可以更完整地回答您的问题。