Converting Linq expression to sql server query - c#

I am using some crm framework and this framework does not have any internal orm and is not using entity framework, only plain sql queries.
I have Entity for each table in the database.
So I have for example:
public class Customer{
public string FirstName{get;set;}
public int Status{get;set;}
}
Is there anyway I can write linq queries and convert them into sql without using entity framework or NHibernate?
I am looking for something like.
IQueryable linq = from LinqProvider.Get<Customer>() int customer where customer.FirstName == "test" and Status > 1;
string sqlQuery = LinqProvider.ToSqlQuery(linq);
//Select * from Customer where FirstName = "test" and Status > 1
I would live to have some advanced functionality like Join sort and aggregation functionality.

Note difference between following 2 lines (linq in lambda form) :
var dataQ = Customer.Where(o=>(o.FirstName == "test" && o.Status > 1);
var dataL = Customer.Where(o=>(o.FirstName == "test" && o.Status > 1).ToList();
var dataS = Customer.SingleOrDefault(o=>(o.FirstName == "test" && o.Status > 1);
As far as I am aware linq queries are converted to lamba and then optimized and auto-compiled (from framework 4.5). By default your database context should have lazy loading and optimistic concurrency turned on. Lazy loading means data is not fetched before you actually need it. In this case .ToList() and SingleOrDefault will force data to be retried. That means they will show up in Entity Framework Profiler.
If you do not want to use it or can not, then you can use ´ToTraceString´, not however it will not work on dataL or dataS because they are not queries, but concrete instances.
File.AppendAllText(traceFile, ((ObjectQuery)dataQ).ToTraceString());
return dataQ.ToList();
Edit
Assumptions that I made:
My assumption is that you can not write proper SQL, but are somewhat familiar with Linq.
You have uncommon database that would use 3rd party provider or you can not even do that.
You (manually?) created mapping for database tables
Now what you can do is use code first approach. You generate database from those classes. Then you query it and you get your SQL. I assumed this is clear. Note that you might want to get code-first-migrations as well, because you most likely need to make changes.
Examples (just google) :
http://msdn.microsoft.com/en-us/data/jj193542.aspx
http://msdn.microsoft.com/en-us/data/jj591621.aspx
Edit 2
Made an example:
https://gist.github.com/margusmartsepp/f9fcc9178600ca53acf6
[Table("CustomerTest")]
public class Customer
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public int Status { get; set; }
}
public class CustomerContext : DbContext
{
public CustomerContext(): base("name=Program.CustomerContext"){}
public DbSet<Customer> Customers { get; set; }
}
//PM> Install-Package EntityFramework
//PM> Install-Package EntityFramework.SqlServerCompact
static void Main(string[] args)
{
using (var db = new CustomerContext())
{
var item = new Customer {FirstName = "test", Status = 2};
db.Customers.Add(item);
db.SaveChanges();
var items = db.Customers.Where(o => (o.FirstName == "test" && o.Status > 1));
Console.WriteLine(items.ToString());
}
Console.ReadKey();
}
Example output:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[Status] AS [Status]
FROM [CustomerTest] AS [Extent1]
WHERE (N'test' = [Extent1].[FirstName]) AND ([Extent1].[Status] > 1)

Related

Concate Two Member Of List In New Select And Bind To DataGridview Column

I wants to join for table from sql with lambda code
i use list for any table and join them
in one table i have "name" and "family" that i need to concat them to veiw fullname in datagrid view but i cat do that
Please guide me and if there is a better solution, I would appreciate it
my code :
var listPartner = db.BusinessPartnerRepository.GetAllPartners();
var listEvidence = db.Evidence.Get();
var listDetail = db.EvidenceDetail.Get();
var listCountry = db.Country.Get();
var result = (from evidence in listEvidence
join detail in listDetail
on evidence.Id equals detail.EvidenceId
join countryname in listCountry
on evidence.CountryId equals countryname.Id
join partner in listPartner
on evidence.PartnerId equals partner.Id
select new
{
evidence.TruckNo,
evidence.SerialNo,
evidence.Date,
partner.Name.Concat(" "+partner.Familly).ToString() ,
detail.Id,
detail.EvidenceId,
detail.MerchandiseName,
detail.weight,
detail.Unit,
detail.Discription,
countryname.CountryName
}).ToList();
dgEvidenceList.AutoGenerateColumns = false;
dgEvidenceList.DataSource = result;
the code of "Get" method:
public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> where = null)
{
IQueryable<TEntity> query = _dbSet;
if (where != null)
{
query = query.Where(where);
}
return query.ToList();
}
the code of "GetAllPartners" method:
public List<BusinessPartners> GetAllPartners()
{
return db.BusinessPartners.ToList();
}
Relation:
the "Evidence" entity have feilds that be foreignKey:
PartnerId ---> Pk in BusinessPartner
CountryId ---> Pk in Country
evidenceId --> Pk in Evidence
You seem to be quite new to C# and Entity Framework. I would advise looking up a few guides.
I am not quite sure if I understand what is your intent. I will try to answer your question as best as I can. Since you are using Entity Framework, you should be interacting with your tables using LINQ and the extension methods entity framework provides. It will make your life easier.
I assume the definition of your class Evidence looks similar to this:
public class Evidence
{
//...
public Detail Detail { get; set; }
public Country Country { get; set; }
public Partner Partner { get; set; }
//...
}
You can then use Include to join your tables together:
List<Evidence> evidences = db.Evidence
.Include(e => e.Detail)
.Include(e => e.Country)
.Include(e => e.Partner).ToList();
You should be able to then use this list of Evidences as data for your DataGrid. However, if you want/need to flatten it to a table of strings, you can do this:
List<List<string>> table = evidences.Select(e => new List<string>()
{
e.TruckNo,
e.SerialNo,
//...
}).ToList();
If you need to join two strings together with a space between, you have many options:
// standard string concatenation
string1 + " " + string2
// string interpolation
$"{string1} {string2}"
I hope I answered your question. Again, I really recommend looking up some C# and Entity Framework guides.

EF Core 5 check if all ids from filter exists in related entities

I have two models:
public class Employee
{
public int Id { get; set; }
public IList<Skill> { get; set; }
}
public class Skill
{
public int Id { get; set; }
}
And I have filter with list of skill ids, that employee should contain:
public class Filter
{
public IList<int> SkillIds { get; set; }
}
I want to write query to get all employees, that have all skills from filter.
I tried:
query.Where(e => filter.SkillIds.All(id => e.Skills.Any(skill => skill.Id == id)));
And:
query = query.Where(e => e.Skills
.Select(x => x.Id)
.Intersect(filter.SkillIds)
.Count() == filter.SkillIds.Count);
But as a result I get exception says that query could not be translated.
It is going to be a difficult, if not impossible task, to run a query like this on the sql server side.
This is because to make this work on the SQL side, you would be grouping each set of employee skills into a single row which would need to have a new column for every skill listed in the skills table.
SQL server wasn't really made to handle grouping with an unknown set of columns passed into a query. Although this kind of query is technically possible, it's probably not very easy to do through a model binding framework like ef core.
It would be easier to do this on the .net side using something like:
var employees = _context.Employees.Include(x=>x.Skill).ToList();
var filter = someFilter;
var result = employees.Where(emp => filter.All(skillID=> emp.skills.Any(skill=>skill.ID == skillID))).ToList()
This solution works:
foreach (int skillId in filter.SkillIds)
{
query = query.Where(e => e.Skills.Any(skill => skill.Id == skillId));
}
I am not sure about it's perfomance, but works pretty fast with small amount of data.
I've also encountered this issue several times now, this is the query I've come up with that I found works best and does not result in an exception.
query.Where(e => e.Skills.Where(s => filter.SkillIds.Contains(s.Id)).Count() == filter.SkillIds.Count);

use Linq to form a relationship where none exists in the database

I've been using the .Net Core Linq expressions .Include and .ThenInclude to great success with my Entity Framework Core project.
However, I need to combine 2 models that are not in any type of relationship in the database.
My SQL query looks like this:
SELECT * FROM selectedEnzymes se
LEFT JOIN enzymeDefinitions ed ON se.selectedEnzymeID = ed.selectedEnzymeID
WHERE se.ecosystemID = 7
selectedEnzymes and enzymeDefinitions are both models.
But there is no relationship between them even though they both contained a selectedEnzymeID. In the database, there is no key.
So I was wondering, is there a way to use Linq to combine two models in such a way if no relationship exists?
Thanks!
You can use the LINQ Join and Select as you do in SQL.
Starting with something like this as models and list of both Classes:
public class SelectedEnzymes
{
public int SelectedEnzymeId { get; set; }
public int EcosystemId { get; set; }
public string OtherPropertySelectedEnzymes { get; set; }
}
public class EnzymeDefinitions
{
public int SelectedEnzymeId { get; set; }
public string OtherPropertyEnzymeDefinitions { get; set; }
}
List<SelectedEnzymes> selectedEnzymesList = new List<SelectedEnzymes>();
List<EnzymeDefinitions> enzymeDefinitionList = new List<EnzymeDefinitions>();
You are able to do something like this:
var query = selectedEnzymesList // table in the "FROM"
.Join(enzymeDefinitionList, // the inner join table
selected => selected.SelectedEnzymeId, // set the First Table Join parameter key
definition => definition.SelectedEnzymeId, // set the Secont Table Join parameter key
(selected, definition) => new { SelectedEnzyme = selected, EnzymeDefinition = definition }) // selection -> here you can create any kind of dynamic object or map it to a different model
.Where(selectAndDef => selectAndDef.SelectedEnzyme.EcosystemId == 7); // where statement
So I was wondering, is there a way to use Linq to combine two models
in such a way if no relationship exists?
In fact, this is similar to the method of obtaining two related tables.
You can directly use the following linq to achieve:
var data = (from se in _context.SelectedEnzymes
join ed in _context.EnzymeDefinitions
on se.SelectedEnzymeId equals ed.SelectedEnzymeId
where se.EcosystemId == 7
select new { se.Name,se.LocationId, ed.Name,ed.CountryId }).ToList();
Here is the result:

How to fetch a list from the database based on another list of a different object type?

I have these two models:
public class Product
{
public int Id { get; set; }
public int ProductGroupId { get; set; }
public int ProductGroupSortOrder { get; set; }
// ... some more properties
public ICollection<ProductInCategory> InCategories { get; set; }
}
public class ProductInCategory
{
public int Id { get; set; }
public int ProductId { get; set; }
public int ProductCategoryId { get; set; }
public int SortOrder { get; set; }
// Nav.props.:
public Product Product { get; set; }
public ProductCategory ProductCategory { get; set; }
}
Some of the Products are grouped together via the property ProductGroupId, and I want to be able to remove whole groups of Products from ProductInCategory in a single Db-query.
The controller method receives a product_id and a category_id, not a ProductGroupId.
For a single Product I have been using this query to remove it from the category:
ProductInCategory unCategorize = await _context.ProductsInCategories
.Where(pic => pic.ProductId == product_id && pic.ProductCategoryId == category_id)
.FirstOrDefaultAsync();
and then:
_context.Remove(unCategorize);
await _context.SaveChangesAsync();
Now, if I have a List<Product> that I want to remove from ProductsInCategories, what would the query look like?
I have tried this, but it fails on the .Any()-bit:
Product product = await _context.Products
.Where(p => p.Id == product_id)
.FirstOrDefaultAsync();
List<Product> products = await _context.Products
.Where(g => g.ProductGroupId == product.ProductGroupId)
.ToListAsync();
List<ProductInCategory> unCategorize = await _context.ProductsInCategories
.Where(pic => pic.ProductId == products.Any(p => p.Id)
&& pic.ProductCategoryId == category_id)
.ToListAsync();
The controller method receives a product_id and a category_id, not a ProductGroupId
The first question is why the method receives product_id while it needs to do something with ProductGroupId.
This smells to a bad design, but anyway, let first translate the product_id to the desired ProductGroupId (this will cost us additional db query):
int? productGroupId = await _context.Products
.Where(p => p.Id == product_id)
.Select(p => (int?)p.ProductGroupId)
.FirstOrDefaultAsync();
if (productGroupId == null)
{
// Handle non existing product_id
}
The rest is a simply matter of accessing the navigation property inside the LINQ to Entities query, which will be translated by EF Core to the appropriate join inside the generated SQL query. No intermediate Product list is needed.
List<ProductInCategory> unCategorize = await _context.ProductsInCategories
.Where(pic => pic.Product.ProductGroupId == productGroupId)
.ToListAsync();
Try changing to the following:
List<ProductInCategory> unCategorize = await _context.ProductsInCategories
.Where(pic => products.Select(p => p.Id).Contains(pic.ProductId)
&& pic.ProductCategoryId == secondary_id)
.ToListAsync();
I could suggest a code fix for what you want, but there's a better solution: not loading the data to begin with.
In your code example, you are loading the data from the database, before then telling EF to delete the loaded items. That's not efficient. There should be no reason to load the data, you should be able to simply execute the query without needing to load data.
As far as I'm aware, Entity Framework is not capable of "conditional deletion" (for lack of a better name), for example:
DELETE FROM People WHERE Name = 'Bob'
If you want to delete items based on a particular column value (other than the entity's ID), you can't rely on Entity Framework unless you want to load the data (which eats performance).
There are two better options here:
1. Execute the SQL query yourself
context.Database.ExecuteSqlCommand(
"DELETE FROM Products WHERE ProductGroupId = " + product.ProductGroupId
);
This is how I've always done it.
Sidenote: I'm expecting comments about SQL injection. To be clear: there is no danger of SQL injection here as product.ProductGroupId is not a string, and its value controlled by the developer, not the end user.
Nonetheless, I do agree that using SQL parameters is good practice. But in this answer, I wanted to provide a simple example to showcase how to execute a string containing SQL.
2. Find a library that enables you to delete without loading.
I only stumbled on this when googling just now. Entity Framework Extensions seems to have implemented the conditional delete feature:
context.Customers.Where(x => x.ID == userId).DeleteFromQuery();
In your case, that would be:
_context.Products.Where(g => g.ProductGroupId == product.ProductGroupId).DeleteFromQuery();
Sidenote:
I've always used Code First, and EF has always generated cascaded deletes for me automatically. Therefore, when you delete the parent, its children get deleted as well. I'm not sure if your database has cascaded deletes, but I am assuming default EF behavior (according to my experience).
Probably the products values is set to null on the previous LINQ query. add another condition to validate that.
.Where(pic => products && pic.ProductId == products.Any(p => p.Id)
&& pic.ProductCategoryId == secondary_id)

How do you make EntityFramework generate efficient SQL queries for related objects?

I am trying to work out how to use the .NET EntityFramework to generate both readable and natural code and efficient SQL query statements when fetching related entities. For example, given the following code-first definition
public class WidgetContext : DbContext
{
public DbSet<Widget> Widgets { get; set; }
public DbSet<Gizmo> Gizmos { get; set; }
}
public class Widget
{
public virtual int Id { get; set; }
[Index]
[MaxLength(512)]
public virtual string Name { get; set; }
public virtual ICollection<Gizmo> Gizmos { get; set; }
}
public class Gizmo
{
public virtual long Id { get; set; }
[Index]
[MaxLength(512)]
public virtual string Name { get; set; }
public virtual Widget Widget { get; set; }
public virtual int WidgetId { get; set; }
}
I want to be able to write code like
using (var wc = new WidgetContext())
{
var widget = wc.Widgets.First(x => x.Id == 123);
var gizmo = widget.Gizmos.First(x => x.Name == "gizmo 99");
}
and see a SQL query created along the lines of
SELECT TOP (1) * from Gizmos WHERE WidgetId = 123 AND Name = 'gizmo 99'
So that the work of picking the right Gizmo is performed by the database. This is important because in my use case each Widget could have thousands of related Gizmos and in a particular request I only need to retrieve one at a time. Unfortunately the code above causes the EntityFramework to create SQL like this instead
SELECT * from Gizmos WHERE WidgetId = 123
The match on Gizmo.Name is then being performed in memory by scanning the complete set of related Gizmo entities.
After a good deal of experimentation, I have found ways of creating the efficient SQL use I am looking for in the entity framework, but only by using ugly code which is much less natural to write. The example below illustrates this.
using System.Data.Entity;
using System.Data.Entity.Core.Objects.DataClasses;
using System.Linq;
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<WidgetContext>());
using (var wc = new WidgetContext())
{
var widget = new Widget() { Name = "my widget"};
wc.Widgets.Add(widget);
wc.SaveChanges();
}
using (var wc = new WidgetContext())
{
var widget = wc.Widgets.First();
for (int i = 0; i < 1000; i++)
widget.Gizmos.Add(new Gizmo() { Name = string.Format("gizmo {0}", i) });
wc.SaveChanges();
}
using (var wc = new WidgetContext())
{
wc.Database.Log = Console.WriteLine;
var widget = wc.Widgets.First();
Console.WriteLine("=====> Query 1");
// queries all gizmos associated with the widget and then runs the 'First' query in memory. Nice code, ugly database usage
var g1 = widget.Gizmos.First(x => x.Name == "gizmo 99");
Console.WriteLine("=====> Query 2");
// queries on the DB with two terms in the WHERE clause - only pulls one record, good SQL, ugly code
var g2 = ((EntityCollection<Gizmo>) widget.Gizmos).CreateSourceQuery().First(x => x.Name == "gizmo 99");
Console.WriteLine("=====> Query 3");
// queries on the DB with two terms in the WHERE clause - only pulls one record, good SQL, ugly code
var g3 = wc.Gizmos.First(x => x.Name == "gizmo 99" && x.WidgetId == widget.Id);
Console.WriteLine("=====> Query 4");
// queries on the DB with two terms in the WHERE clause - only pulls one record, also good SQL, ugly code
var g4 = wc.Entry(widget).Collection(x => x.Gizmos).Query().First(x => x.Name == "gizmo 99");
}
Console.ReadLine();
}
Query 1 demonstrates the 'fetch everything and filter' approach that is generated by the natural usage of the entity objects.
Queries 2,3 and 4 above all generate what I would consider to be an efficient SQL query - one that returns a single row and has two terms in the WHERE clause, but they all involve very stilted C# code.
Does anyone have a solution that will allow natural C# code to be written and generate efficient SQL utilization in this case?
I should note that I have tried replacing ICollection with EntityCollection in my Widget object to allow the cast to be removed from the Query 2 code above. Unfortunately this leads to an EntityException telling me that
The object could not be added to the EntityCollection or
EntityReference. An object that is attached to an ObjectContext cannot
be added to an EntityCollection or EntityReference that is not
associated with a source object.
when I try to retrieve any related objects.
Any suggestions appreciated.
Ok, further digging has let me get as close as I think is possible to where I want to be (which, to reiterate, is code that looks OO but generates efficient DB usage patterns).
It turns out that Query2 above (casting the related collection to an EntityCollection) actually isn't a good solution, since although it generates the desired query type against the database, the mere act of fetching the Gizmos collection from the widget is enough to make the entity framework go off to the database and fetch all related Gizmos - i.e. performing the query that I am trying to avoid.
However, it's possible to get the EntityCollection for a relationship without calling the getter of the collection property, as described here http://blogs.msdn.com/b/alexj/archive/2009/06/08/tip-24-how-to-get-the-objectcontext-from-an-entity.aspx. This approach sidesteps the entity framework fetching related entities when you access the Gizmos collection property.
So, an additional read-only property on the Widget can be added like this
public IQueryable<Gizmo> GizmosQuery
{
get
{
var relationshipManager = ((IEntityWithRelationships)this).RelationshipManager;
return (IQueryable<Gizmo>) relationshipManager.GetAllRelatedEnds().First( x => x is EntityCollection<Gizmo>).CreateSourceQuery();
}
}
and then the calling code can look like this
var g1 = widget.GizmosQuery.First(x => x.Name == "gizmo 99");
This approach generates SQL that efficiently fetches only a single row from the database, but depends on the following conditions holding true
Only one relationship from the source to the target type. Having multiple relationships linking a Widget to Gizmos would mean a more complicated predicate would be needed in the .First() call in GizmosQuery.
Proxy creation is enabled for the DbContext and the Widget class is eligible for proxy generation (https://msdn.microsoft.com/en-us/library/vstudio/dd468057%28v=vs.100%29.aspx)
The GizmosQuery property must not be called on objects that are newly created using new Widget() since these will not be proxies and will not implement IEntityWithRelationships. New objects that are valid proxies can be created using wc.Widgets.Create() instead if necessary.

Categories