Why my SQL query does not work by Entity Framework? - c#

I use his code for executing SQL query in entity framework :
using (var db = new VSServicesEntities())
{
const string selectCmd = #"if exists (Select top 1 IsUserOn From ServiceMembers Where ServiceCode={0} and Number={1})
Select isnull(IsUserOn,0) IsUserOn
From ServiceMembers Where ServiceCode={0} and Number={1}
else
Select Null IsUserOn";
var data = db.ServiceMembers.SqlQuery(selectCmd, "A", 091242535970).ToList();
if (data.Any())
{
var serviceMember = data.First().IsUserOn;
if (serviceMember.ToString() == "")
label1.Text = "";
else
label1.Text = (serviceMember.ToString() == "True" ? "On" : "Off");
}
}
but it gives me an exception :
Must declare the scalar variable "#ServiceCode".
but i give value to ServiceCode= A , what is the problem?
EDIT 2: I edited my query on top, but now it gives me another exception :
The data reader is incompatible with the specified 'VSServicesModel.ServiceMember'. A member of the type, 'ServiceCode', does not have a corresponding column in the data reader with the same name
but my column name is exactly : ServiceCode!! what is the problem??

You aren't passing parameters correctly:
var data = db.ServiceMembers.SqlQuery(selectCmd,
new SqlParameter("#ServiceCode", "A"),
new SqlParameter("#Number", 091242535970)).ToList();
Edit
The type that EF will attempt to assign to data is the type of the element of ServiceMembers (assuming ServiceMember). The only field you return is IsON which does not match the property IsUserOn which you reference. Try instead:
Select isnull(IsUserOn,0) AS IsUserON
...
Select Null AS IsUserON
Note that other properties will not be populated unless you return them from the query with matching names.
Edit You've changed your sql query - the binding was working correctly with ServiceCode=#ServiceCode and Number=#Number. At a guess, the reason why the Reader is complaining is because you are returning just one column (IsUserON) and trying to bind it to your full ServiceMember class. Either return all the columns (especially the primary key, presumably ServiceCode), or alternatively create a new class just with the property IsUserON and then use the generic type overload db.SqlQuery<MyNewPoco>(cmd, ...) to bind it.

Related

ORDER BY sort key(s) type must be order-comparable

Hope someone may be able to help. Trying to implement a jqGrid in ASP.NET MVC 4 C#. I keep getting the following error when trying to sort a column. This column is actually a field from an object using the foreign key. Not sure how the code should be changed to account for this. The grid sorts correctly for the fields that are in the model that I'm using, just not the field that is from the foreign key. Should I be using a ViewModel instead? I'm using the code from Oleg's examples. item.Name is the object and item.Name.FullName is the column I'm trying to sort that is throwing the error below. Thanks!
The ORDER BY sort key(s) type must be order-comparable. Near member access expression, line 6, column 3.
public JsonResult DynamicGridData(string sidx, string sord, int page, int rows, bool search, string filters)
{
var context = new TeamContext();
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
var set = objectContext.CreateObjectSet<Team>();
var serializer = new JavaScriptSerializer();
Filters f = (!search || string.IsNullOrEmpty(filters)) ? null : serializer.Deserialize<Filters>(filters);
ObjectQuery<Team> filteredQuery =
(f == null ? (ObjectQuery<Team>)set : f.FilterObjectSet((ObjectQuery<Team>)set));
filteredQuery.MergeOption = MergeOption.NoTracking; // we don't want to update the data
var totalRecords = filteredQuery.Count();
var pagedQuery = filteredQuery.Skip("it." + sidx + " " + sord, "#skip",
new ObjectParameter("skip", (page - 1) * rows))
.Top("#limit", new ObjectParameter("limit", rows));
// to be able to use ToString() below which does NOT exist in the LINQ to Entity
var queryDetails = (from item in pagedQuery
select new { item.TeamId, item.Year, item.Name }).ToList();
return Json(new
{
total = (totalRecords + rows - 1) / rows,
page,
records = totalRecords,
rows = (from item in queryDetails
select new[] {
item.TeamId.ToString(),
item.Year,
item.Name.FullName
}).ToList()
});
}
This can fail for various reasons:
item.Name.FullName is a binary field in the database?
to access to the Name object through item, lazy loading has to be enabled. are you sure it is?
Anyway using a viewmodel with only data that you need is always a good idea, and is a good way to separate your data Access layer from your controller.

Linq To SQL Select Dynamic Columns

Is it possible to dynamically limit the number of columns returned from a LINQ to SQL query?
I have a database SQL View with over 50 columns. My app has a domain object with over 50 properties, one for each column. In my winforms project I bind a list of domain objects to a grid. By default only a few of the columns are visible however the user can turn on/off any of the columns.
Users are complaining the grid takes too long to load. I captured the LINQ generated SQL query then executed it within SQL Server Management Studio and verified its slow. If I alter the SQL statement, removing all the invisible columns, it runs almost instantly. There is a direct correlation between performance and the number of columns in the query.
I'm wondering if its possible to dynamically alter the number of columns returned from the LINQ generated SQL query? For example, here is what my code currently looks like:
public List<Entity> GetEntities()
{
using (var context = new CensusEntities())
{
return (from e in context.Entities
select e).ToList();
}
}
The context.Entities object was generated from a SQL View that contains over 50 columns so when the above executes it generates SQL like "SELECT Col1, Col2, Col3, ... Col50 FROM Entity INNER JOIN...". I would like to change the method signature to look like this:
public List<Entity> GetEntities(string[] visibleColumns)
{
using (var context = new CensusEntities())
{
return (from e in context.Entities
select e).ToList();
}
}
I'm not sure how to alter the body of this method to change the generated SQL statement to only return the column values I care about, all others can be NULL.
Something like this should work:
List<string> columns = new List<string>();
columns.Add("EmployeeID");
columns.Add("HireDate");
columns.Add("City");
Add columns to your list ^.
var result = Class.ReturnList(columns);
Pass the List to a method ^.
public static List<Entity> ReturnList(List<string> VisibleColumns)
{
StringBuilder SqlStatement = new StringBuilder();
SqlStatement.Append("Select ");
for (int i = 0; i < VisibleColumns.Count; i++)
{
if (i == VisibleColumns.Count - 1)
{
SqlStatement.Append(VisibleColumns[i]);
}
else
{
SqlStatement.Append(VisibleColumns[i]);
SqlStatement.Append(",");
}
}
SqlStatement.Append(" FROM Entity");
using (var ctx = new DataClasses1DataContext())
{
var result = ctx.ExecuteQuery<Entity>(SqlStatement.ToString());
return result.ToList();
}
}
This basically just makes a SELECT statement with all the fields you passed in with the VisibleColumns list.
In this case, the SQL statement that will be generated by the strings in the VisibleColumns list is:
Select EmployeeID, HireDate, City From Employee
(note: i used the Northwind database to try this out, hence the EmployeeID etc column names. You should replace them with your own, obviously.)
It is not trivial to do this dynamically, but if you have a limited set of combinations of columns you want to retreive you can do an explicit select like this:
public List<Entity> GetEntities()
{
using (var context = new CensusEntities())
{
return (from e in context.Entities
select new
{
col1 = e.col1,
col4 = e.col4,
col5 = e.col5,
}
).ToList()
.Select(x=>new Entity{col1 = x.col1, col4 = x.col4, col5 = x.col5}).ToList();
}
}
The extra select step is necessary because LINQ2SQL won't create partial entities for you.
Create a method for each common combination of columns (especially the initial) the users wants to retrieve.
However to make this dynamic you can build a query with you entity stored as a property in an anonymous class and collect your result properties in another anonymous class in second property in the same anonymous class. Finally you select your entities from the collected objects into objects of the correct type.
public List<Entity> GetEntities()
{
using (var context = new CensusEntities())
{
var combinedResult = (from e in context.Entities
select new {
Entity = e,
CollectedValues = new
{
// Insert default values of the correct type as placeholders
col1 = 0, // or "" for string or false for bool
col2 = 0, // or "" for string or false for bool
// ...
col49 = 0, // or "" for string or false for bool
col50 = 0, // or "" for string or false for bool
}
);
// Then copy each requested property
// col1
if (useCol1)
{
var combinedResult = (from e in combinedResult
select new {
Entity = e,
CollectedValues = new
{
col1 = e.Enitity.col1, // <-- here we update with the real value
col2 = e.CollectedValues.col2, // <-- here we just use any previous value
// ...
col49 = e.CollectedValues.col49, // <-- here we just use any previous value
col50 = e.CollectedValues.col50, // <-- here we just use any previous value }
);
}
// col2
if (useCol2)
{
// same as last time
col1 = e.CollectedValues.col1, // <-- here we just use any previous value
col2 = e.Enitity.col2, // <-- here we update with the real value
// ...
}
// repeat for all columns, update the column you want to fetch
// Just get the collected objects, discard the temporary
// Entity property. When the query is executed here only
// The properties we actually have used from the Entity object
// will be fetched from the database and mapped.
return combinedResult.Select(x => x.CollectedValues).ToList()
.Select(x=>new Entity{col1 = x.col1, col2 = x.col2, ... col50 = x.col50}).ToList();
}
}
There will be lots of code, and a pain to maintain, but it should work.
If you are going this route I suggest that you build a code generator that builds this code with reflection from your LINQ context.
Try something like this
using (var context = new CensusEntities())
{
var q = from e in context.Entities
select e.myfield1,e.myfield2;
return q.Tolist();
}
The resulting query should be lighter and also all the data conversion that goes underneath.
But if you really need to build dynamic input, I think some dynamic sql should be involved. So
build the dynamic SQL and get a data table
use a datatable to a dynamic object conversion as shown here
How can I convert a DataTable into a Dynamic object?
BTW a lot of hard work, I think you should considered using the first block of code.

Linq to Sql General Help - Insert Statement

I am currently trying to create a new order (which will be shown below) in a web service, and then send that data to insert a new row into the database. For some reason my DBML / Data Context does not allow me to use InsertOnSubmit.
Any ideas? I haven't used Linq to Sql in about 7 months.
Thanks in advance.
[WebMethod]
public string InsertOrderToDatabases()
{
//Start Data Contexts ------
DataContext db = new DataContext(System.Configuration.ConfigurationManager.AppSettings["RainbowCMSConnectionString"]);
DataContext dcSqlOES = new DataContext(System.Configuration.ConfigurationManager.AppSettings["OESConnectionString"]);
//Get table from local database
Table<Schedule> Schedule = db.GetTable<Schedule>();
//Find last order number in databases
var lastOrderNumber = from lOrder in Schedule
orderby lOrder.templ_idn descending
select lOrder.templ_idn;
int firstOrderID;
var firstOrder = lastOrderNumber.FirstOrDefault();
firstOrderID = firstOrder.Value + 1;
qrOrder qrOrd = new qrOrder
{
.... data in here creating a new order
};
//TODO: fix below with an insert on submit
if (qrOrd != null)
{
// **Schedule.InsertOnSubmit(qrOrd);**
}
//db.GetTable<Schedule>().InsertOnSubmit(qrOrd);
try
{
//Submit the changes to the database
db.SubmitChanges();
return "Orders were sent to the databases.";
}
catch ()
{
}
}
Based on your response, it appears that you are using the wrong table, or perhaps the wrong data type. I also noticed that when you declare your localSchedule variable, you declare it as type Table<Schedule>, which means it should contain Schedule entities, not qrOrder entities.
Table<TEntity>.InsertOnSubmit expects a specific strongly typed entity to be passed in. In your case, it is expecting Web_Service.Schedul‌e, but you are trying to pass in a qrOrder.
Schedule.InsertOnSubmit(qrOrd);
That line will not treat to submit changes to connected entity , Try this
db.Schedule.InsertOnSubmit(qrOrd);
db.SubmitChanges();
you can try with
db.GetTable(typeof(Schedule)).InsertOnSubmit(qrOrd);
Or
db.GetTable(qrOrd.GetType()).InsertOnSubmit(qrOrd);

Is there a way to access the columns in a Dapper FastExpando via string or index?

I am pulling in a Dapper FastExpando object and want to be able to reference the column names dynamically at run time rather than at design/compile time. So I want to be able to do the following:
var testdata = conn.Query("select * from Ride Where RiderNum = 21457");
I want to be able to do the following:
foreach( var row in testdata) {
var Value = row["PropertyA"];
}
I understand that I can do:
var Value = row.PropertyA;
but I can't do that since the name of the property i'm going to need won't be known until runtime.
The answer from this SO Question doesn't work. I still get the same Target Invocation exception. So...
Is there any way to do what I want to do with a Dapper FastExpando?
Sure, it is actually way easier than that:
var sql = "select 1 A, 'two' B";
var row = (IDictionary<string, object>)connection.Query(sql).First();
row["A"].IsEqualTo(1);
row["B"].IsEqualTo("two");
Regarding the portion of the title "or index?" - I needed to access results by index since the column names being returned changed sometimes, so you can use a variation of Sam Saffron's answer like this:
var sql = "select 1, 'two'";
var row = (IDictionary<string, object>)connection.Query(sql).First();
row.Values.ElementAt(0).IsEqualTo(1);
row.Values.ElementAt(1).IsEqualTo("two");
There a simple way to access fields direct below sample
string strConexao = WebConfigurationManager.ConnectionStrings["connection"].ConnectionString;
conexaoBD = new SqlConnection(strConexao);
conexaoBD.Open();
var result = conexaoBD.Query("Select Field1,Field2 from Table").First();
//access field value result.Field1
//access field value result.Field2
if (result.Field1 == "abc"){ dosomething}

Convert DataTable to LINQ: Unable to query multiple fields

Importing a spreadsheet I have filled a DataTable object with that data and returns expected results.
Attempting to put this into a format I can easily query to search for problem records I have done the following
public void Something(DataTable dt)
{
var data = from row in dt.AsEnumerable()
select row["Order"].ToString();
}
Works as expected giving me a list of orders. However I cannot add other fields to this EnumerableRowCollection. Attempting to add other fields as follows gives me an error
public void Something(DataTable dt)
{
// row["Version"] throws an error on me
var data = from row in dt.AsEnumerable()
select row["Order"].ToString(), row["Version"].ToString();
}
Error: "A local variable named 'row' cannot be declared in this scope because it would give a different meaning to 'row' which is already used in a 'child' scope to donate something else"
I'm thinking I need to alias the column name but I'm having no luck. What am I missing here?
It sounds like you're writing a bad select statement. Try the following:
public void Something(DataTable dt)
{
var data = from row in dt.AsEnumerable()
select new {
Order = row["Order"].ToString(),
Something = row["Something"].ToString(),
Customer = row["Customer"].ToString(),
Address = row["Address"].ToString()
};
}
That will create a new collection of Anonymously Typed objects that you can iterate over and use as needed. Keep in mind, though, that you want be able to return data from the function. If you need that functionality, you need to create a concrete type to use (in place of anonymous types).
I think you should use select new like this query for example:
var q = from o in db.Orders
where o.Products.ProductName.StartsWith("Asset") &&
o.PaymentApproved == true
select new { name = o.Contacts.FirstName + " " +
o.Contacts.LastName,
product = o.Products.ProductName,
version = o.Products.Version +
(o.Products.SubVersion * 0.1)
};
You probably want the following.
var data = from row
in dt.AsEnumerable()
select new { Order = row["Order"].ToString(), Version = row["Version"].ToString() };

Categories

Resources