I have a database that gets loaded based on Template T. However, now I want to join other tables based on strings or passing in a "T2" Template.
How can I create a function like this to generate an IQueryable?
public void createJoinedTable<T, T2>(T2 join_table, string join_on_this_property, string order, string order_by)
where T : class
where T2 : class
{
var table = GetGenericTable<T>(); // I have the IQueryable<T> of the main table.
// now join the joined table.
var id = 1;
table = table // your starting point - table in the "from" statement
.Join(join_table, // the source table of the inner join
firsttable => post.myid, // Select the primary key (the first part of the "on" clause in an sql "join" statement)
secondtable => meta.othertableid, // Select the foreign key (the second part of the "on" clause)
(firsttable, secondtable) => new { Unknown = firsttable , Unknown2 = secondtable}) // selection
.Where(x => x.Unknown.ID == id); // where statement
table = table.CustomOrderByDescending(order_by, direction); // custom ordering by string
m_queryable = table; // record results.
}
The problem is, that I cannot do a .Join() because it is not constrained by the Entity class. It's constrained as a generic "class".
Where T : class instead of where T: MyEntityTable
Well, if I did that in the arguments, then what's the point of having a "generic join table function"?
I want to be able to join whatever I want and based on text-based arguments.
How would I use "join_on_this_property" to help me accomplish this?
BONUS Challenge: Join unlimited amounts of tables based on "List tables, List join_ON_properties"--but that could be very complicated.
Related
Table 1
Contract ID , Vendor Name, Description , user
Table 2
Contract ID , product , department
Match condition : for all the Contract ID matching with table 1 , get their Vendor Name and Contract ID
Query result output :
Contract ID(Distinct),Vendor Name
Below code using inner join , need same output without using join as linq query
\\
select table1.Contract ID,table1.Vendor Name ,table2.Contract ID
from table1 as s1
inner join table2 as s2
on s1.Contract ID=s2.Contract ID
\\\
Thanks in Advance
Considering you need only Join alternative to select distinct ,you can use inner query logic like below to write LINQ
SELECT contractorid ,vendor name
Where
Contracterid in
(Select distinct contractor id from table2)
Here assumption is contractorId is primary key in table 1
If I understand correctly, you want to retrieve a collection of objects containing Contract Id and Vendor Names, without duplicates, whose Contract Id is found in Table 2.
It is unclear if you are using Linq to objects, Linq to Entities, or any other Linq flavor, which would have a meaningful impact on how to best construct a query for a specific purpose.
But as a first hint, here is a way to perform this without join with Linq:
// Get a list of all distinct Contract Ids in Table 2
var AllTable2ContractIds = Table2
.Select(e => e.ContractId)
.Distinct()
.ToList();
// With Table 1
// Keep only entries whose contract Id is found in the list just contructed above.
// transform it to keep Contract Id and Vendor Name.
// The use of ToList() at the end is not mandatory.
// It depends if you want to materialize the result or not.
var Result = Table1
.Where(e => AllTable2ContractIds.Contains(e.ContractId))
.Select(e => new
{
e.ContractId,
e.VendorName
})
.ToList();
I met situation I have to write app where I'm taking bunch of records from tableA then for each of record I have to do lookup against tableB to pull extra information (get another 3 columns).
TableA is a small table (<1000 records), but tableB is much bigger. Also, these resides in separate DB on the same DB server.
What would be best approach to get it optimized?
There is no option to get all records into list of objects from tableB then operate on it, rather I would need to run LINQ query for each of tableA element(object) against tableB. This is part of my MVC so could you please provide me an draft of solution, described at high level, rather than providing code.
EDIT
The tableA records need to be "enriched" all against tableB before they are displayed, in effecitve this may be +/- 500 tableA records to be lookup against tableB. Also, limitation is I have only read access to the tableB..no option to write procedures, etc
You could create a view in one of the databases that combines data in table A and B. Then map your entity to that view. Check this out https://www.mssqltips.com/sqlservertip/1990/how-to-use-sql-server-views-with-the-entity-framework/
Without seeing your models and query, it is hard to provide an accurate answer. However, the best start is within the database (I would assume SQL Server 2012+).
From your description, the generated query should look, in a very simplified way, like the following:
SELECT A.*, B.Col1, B.Col2, B.Col3
FROM Db1.dbo.TableA AS A
JOIN Db2.dbo.TableB AS B ON B.Id = A.FkToBId
According to this question and its accepted answer, there is no big difference between selecting from the same database vs. selecting from another database within the same instance.
If TableB is big, you should avoid table scans, so the following index should be a good start:
CREATE INDEX IDX_TableB_Id ON TableB (Id) INCLUDE (Col1, Col2, Col3)
However, if the schema is properly normalized, the lookup key should also be a primary key and this index should not be required. I think that if it is clustered, it might bring extra benefit.
Of course, there is a chance that your LINQ generates a slightly different query. If this is the case, edit your question and include table schema, LINQ and generated query.
[EDIT]
Using SqlQuery is an option, but I am thinking about another way:
1) Generate a database context for each database. Lets call them DbContextA and DbContextB
2) Get only required information from TableB, store it in a dictionary for fast lookups and use in an aggregated query.
var ids = DbContextA.TableA.AsNoTracking().Select(item => item.FkToBId).ToList();
var bInfo = DbContextB.TableB.AsNoTracking()
.Where(item => ids.Contains(item.id))
.ToDictionary(
item => item.Id,
item => new { item.Col1, item.Col2, item.Col3 }
);
var aggrInfo = DbContextA.TableA.AsNoTracking()
.ToList()
.Select(item => new
{
AField = item,
Col1 = bInfo[item.FkToBId],
Col2 = bInfo[item.FkToBId],
Col3 = bInfo[item.FkToBId]
};
If this does not provide the required efficiently, SqlQuery remains the only option, as a database context cannot work with two databases at once (source).
You should create a one class means .cs file and add all the columns of TableA and TableB which is required.
Let see and Example Here i am having two tables category and sub category i want the name of the category and want to show the list.
public class SubCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string Image { get; set; }
public Nullable<bool> Isdisplay_ { get; set; }
public Nullable<int> CatId { get; set; }
public string CatName { get; set; }
}
var data = (from t in db.SubCategories
join ts in db.Categories on t.CatId equals ts.CategoryId
select new { a1 = t.SubId, a2 = t.SubImage, a3 = t.SubIsdisplay_, a4 =
t.SubName, a5 = ts.CategoryName }).ToList();
List<SubCategory> p1 = new List<SubCategory();
foreach (var i in data)
{
SubCategory p2 = new SubCategory();
p2.Id = i.a1;
p2.Name = i.a4;
p2.Image = i.a2;
p2.Isdisplay_ = i.a3;
p2.CatName = i.a5;
p1.Add(p2);
}
return p1;
Now you can use the list p1 to show your data.
In LINQ, I am trying to inner join custom function written for full-text search and an Iqueryable result.
However, I get the following error when I try to to_ret.select(--something--).ToList()
Nested query does not have appropriate key
LINQ Code:
var sql_query = db.search(st);
var to_ret = from ts in sql_query
from t in table
where t.Id == ts.Value select t;
to_ret = to_ret.Include(x => x.table1)
.Include(x=> x.table2.Select(y=> y.table2Col));
to_ret.select(-something-).toList();
SQL Code:
create function [dbo].[search]
(#keywords nvarchar(4000))
returns table
as
return (
select [key] from containstable(tb,(Name,Description),#keywords)
)
Code that works in place of above LINQ Code :
var ids = (from t in table join ts in db.search(st) on t.Id equals ts.Value select t.Id).ToList();
to_ret = to_ret.Where(x => ids.Contains(x.Id));
However, the code that works isn't efficient enough as it eagerly loads all the ids for comparison
Do not join the table using LINQ, it is not effective.
You need to include all the joined functions into [dbo].[search] table valued function (like a view). Then do just call the [dbo].[search] from EF and filter it.
I have mentioned joined Fulltext table valued function here.
Note that fulltext and filtering together in one query could take time, because it is not easy job for query optimizer. Query optimizer selects to perform first fulltext on entire table(s) and then filtering or the opposite way.
I have some sql tables that I need to query information from my current query that returns a single column list is:
from f in FactSales
where f.DateKey == 20130921
where f.CompanyID <= 1
join item in DimMenuItems
on f.MenuItemKey equals item.MenuItemKey
join dmi in DimMenuItemDepts
on item.MenuItemDeptKey equals dmi.MenuItemDeptKey
group f by dmi.MenuItemDeptKey into c
select new {
Amount = c.Sum(l=>l.Amount)
}
This returns the data I want and it groups correctly by the third table I join but I cannot get the Description column from the dmi table. I have tried to add the field
Description = dmi.Description
but it doesnt work. How can I get data from the third table into the new select that I am creating with this statement? Many thanks for any help.
Firstly you are using Entity Framework COMPLETELY WRONG. Linq is NOT SQL.
You shouldn't be using join. Instead you should be using Associations.
So instead, your query should look like...
from sale in FactSales
where sale.DateKey == 20130921
where sale.CompanyID <= 1
group sale by sale.Item.Department into c
select new
{
Amount = c.Sum(l => l.Amount)
Department = c.Key
}
By following Associations, you will automatically be implicitly joining.
You should not be grouping by the id of the "table" but by the actual "row", or in Object parlance (which is what you should be using in EF, since the raison d'etre of an ORM is to convert DB to Object), is that you should be grouping by the "entity" rather than they the "entity's key".
EF already knows that the key is unique to the entity.
The grouping key word only allows you to access sale and sale.Item.Department after it. It is a transform, rather than an operator like in SQL.
I have 2 tables.
Table 1 and Table 2. They have one to many relation. I'm trying to do a query like below. It works fine with finding the result. I mean if it cannot find any result according to the params i got null value as usual. However it brings all Table 2 results all the time in Table 1 class and I do want to get only Table 2 results according to the query.
dc.Table1s.SingleOrDefault(t1 => t1.SearchField1 == param1
&& t1.Table2s.Any(t2 => t2.SearchField2 == param2
&& t2.SearchField3 == param3));
I do want the result as Table1 class and filtered with Table1.Table2s. Is it possible???
You need to define the how you want the relationship between the two tables to be used.
For instance, if Table 2 has a reference to Table 1, and Table 1 has a collection of table 2:
var result = (from t1 in Table1s
from t2 in t1.Table2s // this leverages the relationship
where t1.sf1 == p1
&& t2.sf2 = p2
&& t2.sf3 == p3
select t1).FirstOrDefault();
The above will join the two using your relationship and give you all t1's that match the criteria.