DbExtensions - Cannot Manipulate SqlBuilder - c#

I have an existing SqlBuilder, which looks like this
var query = SQL
.WHERE("Field1 = {0}", "bob");
Now i want to pass this sqlBuilder into another function, so it can be manipulated, where by a SELECT, FROM and additional WHERE clauses can be added to it, but this doesnt seem to be possible?
If i pass this sqlBuilder into a function and call .SELECT like so
public void myFunction(SqlBuilder sqlBuilder)
{
var newBuilder = sqlBuilder.Clone()
.SELECT("*")
.FROM("myTable")
.WHERE("Field2 = {0}", "something");
}
Now i would expect the SQL to be something like
SELECT * FROM myTable WHERE Field1 = 'bob' AND Field2 = 'something'
But instead i get SQL equal to
WHERE Field1 = 'bob' SELECT * FROM myTable WHERE Field2 = 'something'
Obviously this is invalid. How can i do what i want? Is this not possible with DbExtensions?
As i said, i would like to manipulate the existing SqlBuilder (clone it first) and add additional clauses to it. Do you have to add each clause in order? Thought it would hold each in some sort of background dictionary until it needed to create the SQL string?

With SqlBuilder, order matters, just like with StringBuilder. You have to either adapt your code, or use SqlSet instead.

Related

Using sql queries in EF

I am getting data from database in following way:
result = (from d in context.FTDocuments
join f in context.FTDocFlags on d.ID equals f.DocID into fgrp
from x in fgrp.DefaultIfEmpty()
where d.LevelID == levelID && x.UserID == userID && d.Status.Equals(DocumentStatus.NEW)
select new Entities.Document
{
ArrivalDate = d.ArrivalDate.Value,
BundleReference = d.BundleRef,
CreatedDate = d.CreatedDate,
CustomerID = d.CustomerID,
DocType = d.DocType.Value,
GuidID = d.DocGuid,
ID = d.ID,
LastExportID = d.LastExpID,
LevelID = d.LevelID,
ProfileID = d.ProfileID,
ScanDate = d.ScanDate.Value,
ScanOperator = d.ScanOperator,
SenderEmail = d.SenderEmail,
Status = d.Status,
VerifyOperator = d.VerOperator,
FlagNo = x == null ? 0 : x.FlagNo,
FlagUserID = x == null ? 0 : x.UserID
}).ToList();
Now, I am try to achieve this by using sql queries:
var test = context.Database.SqlQuery<string>("select *from FTDocument d left outer join FTDocFlag f on d.ID=f.DocID").ToList();
But get the following error:
The data reader has more than one field. Multiple fields are not valid for EDM primitive or enumeration types
Is it possible to use complex queries like above?
I use EF 6.0.
your query does not return a single string. Use like:
var test = context.Database.SqlQuery<Entities.Document>("...");
try this
const string query = #"select *from FTDocument d left outer join FTDocFlag f on d.ID=f.DocID";
var test = context.Database.SqlQuery<Entity>(query).ToList();
In my answer I have assumed EntitityFramework (ObjectContext) first, but then I have added the code for DbContext as well.
To check out the example below, you can use LinqPad and add your Entity Framework DLL by using EntitityFramework(ObjectContext) via Add connection. Specify connection properties and close the connection dialog. Then select the connection and run the example:
void Main()
{
var context=this; // requires that you selected an EF ObjectContext connection
var q=context.ExecuteStoreQuery<FTDocument>(
"SELECT * FROM FTDocument WHERE ID = #p0", 1);
q.ToList().Dump();
}
It will accept all kind of SQL queries, and you can use parameters like #p0, #p1 etc and simply append them comma-separated when you invoke the function ExecuteStoreQuery. The result will be returned as List<FTDocument>. To convert it to List<string> you need to specify which database field you want to return - or you create a comma-separated list of field values in each row, for example:
q.Select(s=>s.ID+", "+s.GuidID+", "+s.DocType).ToList().Dump();
The same example, but this time with EntityFramework (DbContext):
Add your Entity Framework DLL by using EntitityFramework(DbContext V4/V5/V6) via Add connection. Specify connection properties (don't forget to specify the AppConfig file) and close the connection dialog. Then select the connection and run the example:
void Main()
{
var context=this; // requires that you selected an EF DBContext (V4/5/6) connection
var q=context.Database.SqlQuery<FTDocument>(
"SELECT * FROM FTDocument WHERE ID = #p0", 1);
q.ToList().Dump();
}
Tip: Before you close the connection dialog, click Test. It will save you a headache later if you know the connection succeeds. For all those who want to try it out with a different EF project with your own database, here is a quick tutorial how to create it. Then simply replace FTDocument in the example above by a different table of your choice (in the SQL string and inside the brackets <...> of course).

Name of Table as a Variabe in SQL (C# parameter)

I have a difficult SQL query, and I don't want to rewrite it on Linq. The problem is: I have two equal tables and I must use one of them in depending on the some condition. So, to pass parameter (the name of table) I use this:
List<Variables> lst = db.Database
.SqlQuery<Variables>(s, new SqlParameter("tableSource", sourceTable))
.ToList();
And My query like this:
SELECT #tableSource.PlanId,
#tableSource.PlanSmall AS PlanImg,
#tableSource.NOb,
...
It Doesn't works, could someone help me, please?
You can't use SqlParameter for this. It is meant to be used to set values for parameters in WHERE clauses for instance. You may consider using a construct like
if (someParam.Equals("thisValue")
{
return "SELECT * FROM thisTable";
} else if (someParam.Equals("thatValue")
{
return "SELECT * FROM thatTable";
}

Store query results into object

I try to read sql table and load all into a variable
Code:
String query2 = "";
query2 = String.Format("SELECT * FROM Seguridad.UsuarioPerfil WHERE UsuarioID = {0}", UsuarioID);
SQLService sqlservice2 = new SQLService();
DataTable reader2 = sqlservice.Leer(query2);
I want to store all data into a variable var tmpPerfiles as object.
I can do something like:
var tmpPerfiles ="";
foreach (DataRow row in reader.Rows)
{
tmpPerfiles = row["UsuarioId"].ToString();
tmpPerfiles = row["PerfilId"].ToString();
}
But I canĀ“t call tmpPerfiles two times. How can I achieve that? Regards
Okay, first up: STOP! Do not EVER write SQL queries like this. SQL Injection Attack is still the #1 cause of security breaches and vulnerabilities (per OWASP), and it's exclusively caused by people writing SQL statements like this.
Never ever write SQL statements like:
statement = "SELECT something from sometable where " + someVar ...
... because all it takes is for that 'somevar' to have an apostrophe and some malicious hacking code, and you're granting an external entity access to your database. Don't even do it if you're not expecting the field to be user-provided or such - it's a bad habit, and it leads to horrendous security faults.
Instead, you should always use one of the following:
Stored Procedures with parameterized inputs. Aka, dbo.usp_FindUser,
which accepts #userName, and the proc has WHERE name = #userName
Parameterized Sql Command. Aka, creating a SqlCommand with "Select *
from something from someTable where userName = #userName", and then
adding a parameter to the SqlCommand of userName, and a value of what
user you're looking for.
Okay, all that said?
Keep in mind, a variable can contain a grouping of things. Generally, if you're looking to contain a table within a single variable? It'll typically look something like:
string x, int y, string z - fields within the Database
Class dataRecord - a class, which contains string x, int y, string z.
List<dataRecord> - a list of instances of a dataRecord class
... make sense? You've got one variable per column, which you group into a class. One instance of the class represents one data row. And then a List<> of that class represents multiple rows of that table (or just the whole table itself.)
Usually, code that follows SRP (but that doesn't use EntityFramework) will look something like:
List<myFancyClass> tableEntries = new List<myFancyClass>();
foreach (DataRow dr in myDataTable.Rows)
{
myFancyClass line = new myFancyClass(dr); // constructor that takes in a DataRow
tableEntries.Add(line);
}
... at that point, the table is stored in the tableEntries variable.
e.g.
use a dto for storing it into a list
private class TmpDto {
string UsuarioId { get; set;}
string PerfilId { get; set;}
}
var Ilist<TmpDto> list = new List<TmpDto>();
foreach (DataRow row in reader.Rows)
{
var dto = new TmpDto();
dto.UsuarioId = row["UsuarioId"].ToString();
dto.UsuarioId = row["PerfilId"].ToString();
list.Add(dto);
}
so you have several objects stored within a list
If you are using EntityFramework, this is really easy. After setting up a class for the object (containing all fields), just use linq to get the object.
var myData = UsuarioPerfil.Where(e => e.UsuarioID == UsuarioID).FirstorDefault();
If all you want to do is get values from a database and put them into something, then I think the introduction of a datatable is overkill. While the implementation is easy enough, it adds overhead. I'd opt to use a DbDataReader instead.
This is an example of extracting a single field, and then just adding it to a list.
List<string> results = new List<string>();
String query2 = "SELECT PerfilId FROM Seguridad.UsuarioPerfil WHERE UsuarioID = #USARIO";
SqlCommand cmd = new SqlCommand(query2, connection);
cmd.Parameters.Add(new SqlParameter("#USARIO", SqlDbType.VarChar));
cmd.Parameters[0].Value = UsuarioID;
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
results.Add(reader.GetString(0));
}
reader.Close();
You referenced two fields, but since UsuarioID was defined in the where clause, it didn't seem necessary to pull it back.
Also, as #DotNetDev mentioned, don't use literals... the use of parameters is not only SQL-Injection safe, but it's more scalable and actually friendlier to the database (compile-once, execute many).
Finally, if you want to pull back multiple fields, create a data (domain) object, and make your results a list of that object. If you need an example, feel free to ask.

How use wild card in table name for GetSchema method?

I want query all the existing tables whose name begin with specific string in database using oledb GetSchema method.
In other words I am looking for equivalent of the following sql query:
SELECT * FROM information_schema.tables WHERE table_name LIKE 'customer%'
Something like:
String[] tableRestrictions = new String[4];
tableRestrictions[2] = "%customer";
DataTable customerTables = conn.GetSchema("Tables", tableRestrictions );
In this case the table name I want to query from is Dynamic. Therefore I needed to get the whole table name first.
It seems there is not efficient way to do that but using an iteration.
I came to this conclusion that using Linq provides the neatest solution(Thanks to Tim's answer to a similar case):
// Get the tables using GetSchema:
dtbTables = dbConnection.GetSchema("tables");
// use linq to get the table whose name matches the criteria:
DataRow recSpecificTable = dbConnection.GetSchema("Tables").AsEnumerable()
.Where(r => r.Field<string>("TABLE_NAME")
.StartsWith("customer")).FirstOrDefault();
// Now the table name I am looking for:
tableName = Convert.ToString(recSpecificTable["TABLE_NAME"]).Trim();

How can I create a LINQ-to-SQL statement when I have table name as string?

If I have the name of a database table like this:
string tableName = "Addresses";
string tableName = "Customers";
How can I construct a dynamic LINQ statement like this:
var items = from o in db.{tableName}
select o;
foreach (var item in items)
{
sb.Append(item.Id + Environment.NewLine);
}
I know I could do something like this:
IEnumerable<Customer> results = db.ExecuteQuery<Customer>
("SELECT contactname FROM customers WHERE city = {0}",
"London");
But in this instance I don't want strongly typed objects as my result, I just want a recordset to pick apart.
Answer:
Thanks Shalkalpesh, I took your advice and solved this by just avoiding LINQ altogether:
SqlConnection conn = new SqlConnection();
conn.ConnectionString = ConfigurationManager.ConnectionStrings["main"].ToString();
conn.Open();
string sql = "SELECT * FROM " + tableName;
SqlDataAdapter da = new SqlDataAdapter(sql, conn);
DataTable dtResult = new DataTable();
da.Fill(dtResult);
foreach (DataRow drRow in dtResult.Rows)
{
Console.WriteLine(drRow["Id"].ToString());
}
da.Dispose();
conn.Close();
conn.Dispose();
If you want the recordset, you can access the Connection property of the DataContext class (db variable in your context) and use it to execute regular query and get the result in either of DataTable or DataReader.
You can use the Dynamic Linq Query library (or D-Linq for short).
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Sorry - I'm away from a dev machine at the moment, but would this help?
It seems to suggest you should use DynamicQuery ...
Another way - as was mentioned by
several commenters in my previous post
- is to use DynamicQuery. DynamicQuery is one of the samples installed with
the 101 LINQ samples and you can find
it by clicking on Help | Samples in
Visual Studio. If you drill into the
sample folders there's a DynamicQuery
sample project, which basically
consists of a class that provides
string based lambda expression
parsing.
The class DynamicQuery class is self
contained and you can simply add it to
your project. It provides additional
extension methods that let you use
string expressions for various of the
query methods including the .Where()
method (but unfortunately for the
above example not the .Single()
method). So with Dynamic Query the
above .Load() method can also be
written as follows:
There's an actual code example on the post, too...
LINQ to SQL is meant to be strongly typed so I don't think you can use LINQ to SQL to use dynamic table names unless you use ExecuteQuery
Thanks
I don't think Dynamic Linq is the solution here.
As far as I know, there is no solution to your problem.
Even with dynamic linq, the compiler would need to somehow figure out what table the string refers to at compile time to allow strong typing of its members.
For instance, let's say you have two tables:
Product {Id, Name, Value}
Customer {Id, Firstname, Surname, Address, Email, ...}
And you use Linq-to-SQL as your ORM:
var items = from p in MagicTableResolver("Product")
where p.Firstname // <-- How could intellisense allow this?
select p;
var items = from c in MagicTableResolver("Customer")
where c.Name // <-- It can't, it cannot be strongly typed
select c;
Building off of this and this, here's how to run some some LINQ commands on a string tablename. I haven't figured out how to get the query syntax working (like "FROM" and "SELECT"), but you can still get and insert rows.
Type tableType = Assembly.GetExecutingAssembly().GetType("NameSpace.TableName");
ITable itable = dbcontext.GetTable(tableType);
//prints contents of the table
foreach (object y in itable) {
string value = (string)y.GetType().GetProperty("ColumnName").GetValue(y, null);
Console.WriteLine(value);
}
//inserting into a table
dynamic tableClass = Activator.CreateInstance(tableType);
//Alternative to using tableType
dynamic tableClass = Activator.CreateInstance(null, "NameSpace.TableName").Unwrap();
tableClass.Word = userParameter;
itable.InsertOnSubmit(tableClass);
dbcontext.SubmitChanges();
//sql equivalent
dbcontext.ExecuteCommand("INSERT INTO [TableName]([ColumnName]) VALUES ({0})", userParameter);

Categories

Resources