考虑以下具有2个可选变量的函数
public ListSelectSqlItems ( string settingsgroup = null, int? state = null) { SqlCommand selectCommand = null; if (settingsgroup == null) { selectCommand = new SqlCommand( "select * from ApplicationSettings ", con); } else { selectCommand = new SqlCommand( string.Format( "select * from ApplicationSettings where settingsgroup='{0}' ", settingsgroup), con); } if (state != null) { selectCommand.CommandText += !selectCommand .CommandText .ToLower() .Contains("where") ? string.Format("where state={0}", state) : string.Format("and state={0}", state); } //etc.. }
我有4种可能性:
settingsgroup==null && state==null settingsgroup==null && state!=null settingsgroup!=null && state==null settingsgroup!=null && state!=null
从上面的每个案例中,都必须生成不同的SQL命令.C#中的内置功能可以帮助我在没有大量条件语句的情况下实现这些功能,如果您要编写上述内容,除了必须将函数重载4次之外,您将如何编写它?
这是SQL中的一个常见问题,可以在查询本身中有效处理,从而允许事先创建查询,使用参数,并通过存储过程进行访问.
使用参数是一项重要建议,不应视为可选.SQL参数将有助于防止SQL注入攻击.例如,假设有人使用以下参数值调用您的方法:
SelectSqlItems<T>("' OR settingsgroup <> '", null);
您的查询现在将成为:
select * from ApplicationSettings where settingsgroup='' OR settingsgroup<>''
这当然会返回表中的所有行,并可能暴露私人信息.但是,存在更糟糕的可能性,例如插入DELETE
可能删除整个表的子句,甚至删除整个数据库(尽管希望您的用户权限配置为至少可以防止这些最坏情况).
为防止这种情况,您的SelectSqlItems
方法可以重述为以下内容:
public List<T> SelectSqlItems<T>( string settingsgroup = null, int? state = null) { var cmdText = "..."; // See Query Below var selectCommand = new SqlCommand(cmdText, con); // Set the values of the parameters selectCommand.Parameters.AddWithValue("@settingsgroup", settingsgroup); selectCommand.Parameters.AddWithValue("@state", state); // etc... }
您的查询现在可以说明如下:
SELECT * FROM ApplicationSettings WHERE ((@settingsgroup IS NULL) OR (settingsgroup=@settingsgroup)) AND ((@state IS NULL) OR (state=@state))
如果参数值为null,则连接的条件语句的左侧OR
将始终具有该值TRUE
,因此将匹配所有行.但是,如果参数值不是NULL
,则条件的左侧将具有该值,FALSE
并且将检查右侧.TRUE
如果行的值与参数值匹配,则右侧仅具有值,因此仅返回与参数值匹配的行.可以根据需要使用尽可能多的参数重复该概念.