Fetching complex objects by raw SQL query in Entity Framework - c#

I would like to fetch from database complex object using single query. Let's look at the following example:
SELECT TableA.*, TableB.*
FROM TableA
INNER JOIN TableA.B_Id = TableB.Id
and corresponding classes:
public class QueryResult
{
public TableA A { get; set; }
public TableB B { get; set; }
}
public class TableA
{
public int Id { get; set; }
public string SomeContentA { get; set; }
public int B_Id { get; set; }
}
public class TableB
{
public int Id { get; set; }
public int SomeContentB { get; set; }
}
I would like to execute the raw SQL query from above against the database and get collection of QueryResult objects with correctly set A and B properties. So far I tried using SqlQuery method, but I only managed to get collection of QueryResult objects with nulls in A and B properties (apparently returned result set was not correctly binded to properties):
var results = ctx.Database.SqlQuery<QueryResult>(\\example_query).ToList();
Note that:
I shouldn't list manually columns in SELECT statement. TableA and TableB classes and SQL tables are likely to change over time, but those changes will be consistent.
Three queries (one to fetch IDs from TableA and TableB, second to fetch objects from TableA, third for objects from TableB) will hurt performance and I should try avoid it if possible.
I am using Entity Framework 4.3 and SQL Server 2012.
Thanks,
Art

You can still use regular EF constructions by just mapping your classes to their corresponding tables and forcing the join in LINQ-To-Entities:
using(var ctx = new MyDbContext())
{
return ctx.TableA
.Join(ctx.TableB, a=>a.B_Id, b=>b.Id, (a,b)=>
new QueryResult{TableA=a, TableB=b});
}
I think that's the only way, at least up to EF6.

Related

How to return data from different tables using Sql Query in EF Core without using table valued functions/Linq

How can i select data from two tables using SQL query using ef core.
Below are two tables
public class Category
{
public int Id { get; set; } // Don't want to return this.
public string Name { get; set; } // Only want to return this.
}
public class Product
{
public int ProductId {get;set;}
public string Name { get; set; }
public int CategoryId {get;set;}
}
I want to execute Select P.Name , C.Name from tblCategory C JOIN tblProduct P ON C.Id = P.CategoryId
in entity framework core.
One option is to use table valued functions but i dont want to consider it?
I dont want to use linq because the sql generated is not efficient.
Above is just an example not a real scenario.
If you want to do this using EF Core instead of ADO.NET or Dapper, you can use raw Sql Queries as below:
EF Core 2.1 example:
var blogs = context.Blogs
.FromSql("SELECT * FROM dbo.Blogs")
.ToList();
In EF Core 3.1, FromSql is Obsolete, Hence use FromSqlRaw
var blogs = context.Blogs
.FromSqlRaw("SELECT * FROM dbo.Blogs")
.ToList();
Similarly, your query can be executed as
var results = context.ProductCategories.FromSqlRaw(Select P.Name as ProductName, C.Name as CategoryName from tblCategory C JOIN tblProduct P ON C.Id = P.CategoryId)
Note: Also, you need to define ProductCategories in context class.
public DbQuery<ProductCategory> ProductCategories { get; set; }
ProductCategory.cs
public class ProductCategory
{
public string ProductName { get; set; }
public string CategoryName { get; set; }
}
Here, ProductCategory is known as query type in EF Core 2.1.
In EF Core 3.0 the concept was renamed to keyless entity types. It serves as return type for raw Sql Queries.
You can refer learn.microsoft.com for more details
You can just execute an SQL statement with the query that you want:
using (var connection = new SqlConnection(_ConnectionString))
{
connection.Open();
{
string query = #"select P.Name, C.Name from tblCategory C JOIN P ON C.Id=P.CategoryId";
result = (await connection.QueryAsync<MyDto>(query)).ToList();
}
}
In EF you would normally not use a raw SQL query for something so simple.
Instead, add a Navigation Property to from Product to Category:
public class Category
{
public int Id { get; set; } // Don't want to return this.
public string Name { get; set; } // Only want to return this.
}
public class Product
{
public int ProductId {get;set;}
public string Name { get; set; }
public Category Category {get;set;}
public int CategoryId {get;set;}
}
Then you can write a LINQ query like
from p in db.Product
select new {ProductName = p.Name, CategoryName = p.Category.Name };
In Code-First, this will also ensure that you can't have a Product whose CategoryId is not the Id of some Category by creating a Foreign Key in the database, and (at least for SQL Server) create a non-clustered index on Product.CategoryId for improved performance.

How does one sort using Entity Framework Dynamic "SortBy" string on a complex object?

I am writing an entity framework query that maps to a complex object of objects. I am using jquery datatables which passes over the query sort dynamically as a string typical like "item_id" or "item_id DESC". When passed into a SoryBy clause, it sorts the database results be the item id column. This is fine if that is the base object, but if I have a wrapper object which contains the object, it says item id is not defined for type ItemInfo.
A mockup of the classes are as follows.
public class Item {
public int item_id { get; set; }
public int category_id { get; set; }
}
public class ItemCategory {
public String category { get; set; }
public int category_id { get; set; }
}
public class ItemInfo {
public Item item { get; set; }
public ItemCategory category { get; set; }
}
What does the string for the .SortBy clause need to be to get the item_id from Item in an ItemInfo query?
IQueryable<ItemInfo> query = (from i in Item
join c in ItemCategory on i.category_id equals c.category_id
select new ItemInfo() {
item = i,
category = c
};
Simpler scenario with pure Linq:
If your scenario is simpler and you don't need dynamic at all, you can just do like this:
query = query.OrderBy(i => i.item.item_id);
// OR
query = query.OrderByDescending(i => i.item.item_id);
..which would use the property itself.
Dynamic scenario with "Dynamic Linq"
If you really needed dynamic (which means you would have to order by any fields and didn't want to put a lot of ifs in your code), then you would need to install this package System.Linq.Dynamic by running this in the Package Manager Console:
Install-Package System.Linq.Dynamic
Then you add this using clause:
using System.Linq.Dynamic;
Then you can achieve dynamic ordering with the following:
query = query.OrderBy("item.item_id DESC"); // keep the IQueryable
// OR
var items = query.OrderBy("item.item_id DESC").ToList(); // order in database...
When using this dynamic library, there is no OrderByDescending, since you can pass ASC or DESC with the string passed to the OrderBy() method (as in my example).
Take note item is the same name of the property inside ItemInfo. Also, ToList() is just an example, actually you don't need it at all, depending on your scenario.
Although I'd suggest you to change your ItemInfo to be like this:
public class ItemInfo {
public int item_id { get; set; }
public int category_id { get; set; }
}
...then fix your query to populate accordingly:
IQueryable<ItemInfo> query = (from i in Item
join c in ItemCategory on i.category_id equals c.category_id
select new ItemInfo() {
item_id = i.item_id,
category_id = c.category_id
};
query.OrderBy("item_id DESC");
This looks simpler and cleaner in my point of view. I'd avoid to put the whole object inside ItemInfo, keeping there exactly the properties you need, so you wouldn't load all the fields unnecessarily from database every time.

Entity Framework Code First Stored Procedures with Multiple Result Sets and custom entity

My stored procedure returns two sets of result.
ProductSearchResult and ProductSizeResult are not entities in my case, hence i get an exception The EntitySet name 'Database.ProductSearchResult' could not be found.
I don't want to create Entity in dbcontext for each of my procedure results, is there any solution to map stored procedure to custom objects.
try
{
DbContext.Database.Connection.Open();
DbDataReader reader = cmd.ExecuteReader();
result = ((IObjectContextAdapter)DbContext).ObjectContext.Translate<ProductSearchResult>(reader, "ProductSearchResult", MergeOption.AppendOnly).ToList();
reader.NextResult();
productSizeResults = ((IObjectContextAdapter)DbContext).ObjectContext.Translate<ProductSizeResult>(reader, "ProductSizeResult", MergeOption.AppendOnly).ToList();
}
catch (Exception ex)
{
log.Error(ex);
}
finally
{
DbContext.Database.Connection.Close();
}
My custom entities,
public class ProductSearchResult
{
public int Id { get; set; }
public string Name { get; set; }
public int AvailableQuantity { get; set; }
public int Price{ get; set; }
}
public class ProductSizeResult
{
public int Id { get; set; }
public string Size { get; set; }
public int Count { get; set; }
}
My Stored Procedure,
ALTER PROC GetProductResult #PrimaryCategory nvarchar(100)
AS
select P.Id
,P.Name
,PI.AvailableQuantity
,PI.Price
from Products P
inner join ProductInventories PI on P.Id = PI.ProductId
--where clause
select CA.Name Size,count(1) Count
from Products P
inner join ProductInventories PI on P.Id = PI.ProductId
inner join CustomAttributes CA on PI.CustomAttributeID = CA.Id
--where clause
group by CA.Name
As per MSDN:
The Translate<TElement> method enables you to execute a standard ADO.NET query against a data source and translate the returned data rows into entity objects.
(my emphasis)
That means that the types ProductSearchResult and ProductSizeResult must be mapped types (entity types). This fact was already revealed --more or less-- by the MergeOption parameter. That is about how to add the objects to the change tracker, which doesn't make sense for non-entity types.

linq query to get dependent data in one connection using entity framework 6

I have the following SQL Statements:
DECLARE #UserID INT
SELECT Col1, Col2, Col3, #UserID = UserID
FROM Users
WHERE UserName = <username> AND Password = <password>
SELECT Col1, Col2, Col3
FROM ClientUser
WHERE UserID = #UserID
There are two select statements in the above queries, one to get the UserID,other column information from Users table and second one to get information from another table based on #UserID value. How to write a linq query on entity framework context to do this selection without connecting to database multiple times?
Lets assume that you have two classes, User and ClientUser that are part of your entity framework model. They might look something like this.
public class User
{
public int UserId { get; set; }
public string Col1 { get; set; }
//More to be here
public virtual ICollection<ClientUser> Clients { get; set; }
}
public class ClientUser
{
public int ClientId { get; set; }
public int UserId { get; set; }
public virtual User User {get; set;}
}
The default situation would have you working with lazy loaded since I have these defined as Virtuals. If you know you need more, just do something like this
var query = YourContext.Users.Get().Include(x => x.Clients);
Then add what you need to the query. By using "include" it ensures that EF pulls the data eagerly rather than lazily.

join diffrent field type in linq

join diffrent field type in linq
public partial class Product
{
public int ID { get; set; }
public string CategoryID
{
get { return Myclass.increse(CategoryID); }
set { CategoryID = value; }
}
public string Name { get; set; }
}
public partial class ProductCategory
{
public int ID { get; set; }
public string Name { get; set; }
}
var query = (from c in dContext.ProductCategories
join p in dContext.Products
on Myclass.EncodeMD5(c.ID.ToString()) equals p.CategoryID
select new { id = p.ID, cat = p.CategoryID, name = p.Name, cat1 = c.Name }
).ToList();
The field should be converted to string
Then function runs EncodeMD5
error:
LINQ to Entities does not recognize the method 'System.String
EncodeMD5(System.String)' method, and this method cannot be translated
into a store expression.
You cannot call arbitrary .NET methods in LINQ-to-(some database backend) - the entire point of EF (etc) is that it wants to create SQL from your expression - something involving a where clause. It can work with simple properties and operators, and a few methods it knows about and can map into SQL, but it can't perform something it has never heard of (increse, EncodeMD5, etc) how would it know what SQL to write?
With something like MD5, your best bet would be to store the MD5 hash in the underlying table along with the ID. Likewise with the CategoryID's "increse" (whatever that is). So your query would end up working off these pre-calculated values:
on c.IDHash equals p.CategoryIDHash

Categories