Using a class in joining to database table with EF - c#

If I have a summary class of some objects I need ( for example its primary key, etc..) is there a way to use a list of that class object when I am writing a joining to other tables? so all the things in my LINQ query are real table like this.Context.MyTable but one of them be my List<MyClass> ?
or is there any LINQ related Nuget project that makes this possible?

The EF LINQ queries aren't actually code that is run in C#, they are converted to SQL and run on the database server; so you can't join them with an in-memory array (or List<T>). What you can do, is use Contains like so:
public IEnumerable<Table1> GetThingsFromDatabse(DataContext db, IList<MyObject> objects)
{
var ids = objects.Select(x => x.Id).ToList();
var results = Enumerable.ToList(
from x in db.Table1s
where ids.Contains(x.Id)
select x
);
return results;
}
This gets translated into SQL that looks like this:
SELECT *
FROM [Table1] x
WHERE x.Id IN (#Id1, #Id2, ...)

Related

Linq how to use a subSelect as a virtual table in FROM

There may be similar questions out here but none that I could find for doing a subSelect in the FROM clause as a virtual table.
Most of the columns I need are in one table. There are a few columns needed from different tables that I cannot join on without getting a Cartesian join.
Here is my SQL query:
SELECT meter_name, a.loc_id, a.loc_name, a.facility_name, meter_type
FROM meter_table, (SELECT loc_id, loc_name, facility_name
FROM facility_table
WHERE id = 101) a
WHERE meter_id = a.fac_id
I have no idea how to convert this into Linq and it must be done tonight for a demo in the morning.
Assume this represents your meter_table within your database
in this case each element of the list represents a record in the database table holding the appropriate attributes
i.e the table columns will become the properties of each object
List<Meter> meter_table = new List<Meter>();
Assume this represents the facility_table table you want to join with.
same goes here, each element of the list represents a record in the database table holding the appropriate attributes
i.e the table columns will become the properties of each object
List<Facility> facility_table = new List<Facility>();
then perform the inner join like so:
var query = from m in meter_table
join a in facility_table on m.meter_id equals a.fac_id
where a.id == 101
select new { meter_name = m.MeterName,
loc_id = a.LocId,
facility_name = a.FacilityName,
meter_type = m.MeterType
};
where m.MeterName, a.LocId, a.FacilityName, m.MeterType are properties of their respective types.
it's also worth noting the variable query references an IEnumerable of anonymous types. However, if you want to return an IEnumerable of strongly typed objects then feel free to define your own type with the appropriate properties then just change select new to:
select new typeName { /* assign values appropriately */}
of the above query.

Entity Framework: Precompiled Query for Enumerable.Contains

Entity Framework 5+ is supposed to precompile all queries. However, for queries such as
List<Guid> ids;
var entities = context.MyEntities.Where(x => ids.Contains(x.Id)).ToArray();
Entity Framework cannot precompile the query, and depending on the complexity of the overall query, the parsing of the expression tree to SQL can consume several seconds. Has anyone found a workaround to get a precompiled query anyway? I do not really understand why it would be so hard; of course it is difficult to do with paramters, since the number of elements can differ, but it would be good enough to have SQL like
SELECT a, b, c from MyEntities
WHERE c in __PLACEHOLDER__
and then to substitute the placeholder with the actual list elements. Of course, it is not as nice as passing parameters, but it would be by far better than waiting for seconds for parsing the entire expression tree over and over.
You have to first understand how "IN" operator works in parameterized SQL query.
SELECT A FOM B WHERE C IN #p
does not work, SQL command parameter does not accept ARRAY as a parameter value, instead the query is translated to
SELECT A FROM B WHERE C IN (#p1, #p2, #p3 ... etc)
This query has variable number of parameters and this the reason, there is no way to precompile this query with IEnumerable.Contains.
The only other alternative (long long way) is to use Xml or Json (Coming up in Sql 2016).
Save your IEnumerable as xml.
[10,20,20,50] can be translated to
<data>
<int value="10"/>
<int value="20"/>
<int value="20"/>
<int value="50"/>
</data>
And you can then define a VIEW with parameters as
SELECT A FROM B WHERE C IN (SELECT INT FROM Xml(#P1))
And you can use this View, however there are more challenges in EF to how to fire this query, but this query can be precompiled as it has only one parameter.
Custom SQL for Performance Hack
For pretty simple query like,
List<Guid> ids;
var entities = context.MyEntities.Where(x => ids.Contains(x.Id)).ToArray();
I could simply use a custom SQL and fire,
var parameterList = ids.Select(
(x,i)=> new SqlCommandParameter(
"#p"+i, x));
var pnames = String.Join(",", parameterList.Select(x=> x.ParameterName));
var entities =
context.SqlQuery<MyEntity>(
"SELECT * FROM TABLE WHERE Id in (" + pnames + ")",
parameterList.ToArray());
Temporary Table
You can also use a temporary table, but this increases number of active transactions in your database.
Guid sid = Guid.NewGuid();
foreach(var p in ids){
db.TempIDs.Add(new TempID{ SID = sid, Value = p });
}
db.SaveChanges();
var qIDs = db.TempIDs.Where( x=> x.SID == sid );
var myEntities db.MyEntities.Where( x => qIDs.Any( q.Value == x.Id) );
// delete all TempIDs...
db.SqlQuery("DELETE FROM TempIDs WHERE SID=#sid,
new SqlCommandParameter("#sid", sid));

EF6 Getting Count In Single Call

Try to do a single database call to get an entity, as well as the count of related child entities.
I know I can retrieve the count using
var count = Context.MyChildEntitySet.Where(....).Count();
or even MyEntity.ListNavigationProperty.Count()
But That means getting the entity first, followed by another call in order to get the count or use an Include which would retrieve the whole list of related entities.
I am wondering is it possible to add a "Computed" column in SQL Server to return the Count of related rows in another table?
If not how do I ask EF to retrieve the related count for each entity in once call?
I am thinking of possibly using Join with GroupBy, but this seems an Ugly solution/hack.
public class MyEntity
{
public uint NumberOfVotes{ get; private set; }
}
which ideally woudl generate SQL Similar to:
SELECT
*,
(SELECT Count(*) FROM ChildTable c WHERE c.ParentId = p.Id) NumberOfVotes
FROM ParentTable p
UPDATED
You can always drop down to using actual SQL in the following way...
string query = "SELECT *,
(SELECT Count(*) FROM ChildTable c
WHERE c.ParentId = p.Id) as NumberOfVotes
FROM ParentTable p";
RowShape[] rows = ctx.Database.SqlQuery<RowShape>(query, new object[] { }).ToArray();
I realize this is not ideal because then you are taking a dependency on the SQL dialect of your target database. If you moved form SQL Server to something else then you would need to check and maybe modify your string.
The RowShape class is defined with the properties that match the ParentTable along with an extra property called NumberOfVotes that has the calculated count.
Still, a possible workaround.

Get SQL statement from a predicate?

Is it possible to build the 'WHERE' clause of a SQL statement based on a predicate?
For example:
public override IQueryable<Customer>
SearchFor(Expression<Func<Customer, bool>> predicate)
{ }
The base method just uses EF and all it does is:
return dbSet.Where(predicate);
However, in this particular scenario I need to override the method and, based on the predicate parameter, build a sql statement and execute that statement against the database directly (skipping EF).
So my new method would be:
public override IQueryable<Customer> SearchFor(Expression<Func<Customer, bool>> predicate)
{
var where = predicate.ToString(); //Not actual code!!
var sql = "SELECT id, name FROM customers WHERE " + where;
//Execute sql statement
}
And the caller would do:
var customers = customerRepository.SearchFor(x => x.CustomerType = "ABC" && x.Age > 21);
The customer entity in this example is just an example. The reason for me to build an sql statement instead of using EF is:
Use Dapper for performance.
I will execute a stored procedure to fetch the reocrds.
The entities I'm using are not mapped to a table. A table exists in the database but these entities are just placeholder POCO's for when retrieving records.
Is it possible?
Take a look at DataContext.GetCommand Method
Per MSDN:
Gets the information about SQL commands generated by LINQ to SQL.
This will get you the whole SQL, but some string manipulation will get you just the Where clause.
I wrote code to convert to SQL.
https://github.com/phnx47/MicroOrm.Dapper.Repositories - Include SqlGenerator
https://github.com/phnx47/MicroOrm.SqlGenerator
Example:
var account = accountsRepository.FindAsync(x => x.Id == id)
Generated:
SELECT Accounts.Id, Accounts.Name, FROM Accounts WHERE Accounts.Id = #Id

Querying against multiple columns in Entity Framework 6

I'm trying to execute a query against multiple columns in EF 6 (using C# WPF on VS 2013), I need to select all fields of some columns. I really don't know much but I already tried to do it with Linq and it doesn't seem to have that functionality, so I went with <context>.Database.SqlQuery<string>(query), but it's unclear to me how should I handle what it returns. The query is something simple like "SELECT column1,column2 FROM table".
Is it possible to do it with Linq? How? And for the SqlQuery() case, How should I handle it's result, being most of its columns are in string format?
#MiloGP yes you can do it with using Lambda Expression with LINQ
Here a example:
I have 5 columns in table employee(emp_id,emp_name,emp_dob,emp_address,emp_reference)
and My DBContext name : EmployeeEntities;
I trying to get emp_name and emp_address
List<employee> = EmployeeEntities.employees.select( x => new { x.emp_name, x.emp_address }).ToList();
if you need to get value of someone, As a example emp_id == 13458
List<employee> = EmployeeEntities.employees.Select( x => new { x.emp_name, x.emp_address }).Where( y => y.emp_id == 13458).ToList();

Categories