i am trying to run LINQ query. I have 2 tables with many-to-many relationship and i break them into one-to-many relationship by introducing another table in between, holding primary keys of both table to ensure 3 normal form. now i have mapping class for both table in asp.net but not in between table... i been suggested in previous blog that i don't need to map in-between table and don't need to add that in dbContext-- DbSet either, if i understood correctly!
I want LINQ query where i get all the records from table-Y where table-X ID(PK) == 2... i am aware that can be done in sql query but struggling with LINQ ....
Many thanks in advanced...
mapping classes for 1:* relationship in Entityframework - ASP.NET MVC
Assume table-X holds posts and table-Y holds tags:
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
In this case EF will generate junction table TagPosts for you and query to get all posts which are linked with tags having Id equal to 2 will be:
var query = db.Posts.Where(p => p.Tags.Any(t => t.Id == 2));
Generated SQL query will look like
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title]
FROM [dbo].[Posts] AS [Extent1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [dbo].[TagPosts] AS [Extent2]
WHERE ([Extent1].[Id] = [Extent2].[Post_Id]) AND
(2 = [Extent2].[Tag_Id])
)
As you can see, EF is smart enough to check ids in junction table.
Related
I have two data models:
public class SSC {
public int Id { get; set; }
public string Name { get; set; }
//other irrelevent properties removed for brevity
public virtual ICollection<SpecReference> SpecReferences { get; set; }
}
public class SpecReference {
public int Id { get; set; }
public string Name { get; set; }
//other irrelevent properties removed for brevity
public virtual ICollection<SSC> SSCs { get; set; }
}
I have defined a many-to-many relationship between the two in my data context as:
modelBuilder.Entity<SSC>().HasMany(a => a.SpecReferences)
.WithMany(b => b.SSCs)
.Map(c => {
c.MapRightKey("SpecReference_Id");
c.MapLeftKey("SSC_Id");
c.ToTable("SpecReferenceSSCs");
});
At one point in my site, I have an advanced search page from which users can search for SSCs by a combination of any field in the SSC or any navigation field attached to the SSC. Every search is working except when a user specifies a SpecReference. The query code (abbreviated to show only relevant code) is:
var query = _dbContext.SSCs.Where(x => !x.IsDeleted);
if (designSpecificationIds != null && designSpecificationIds.Any()) {
var designSpecificationIdsHash = new HashSet<int>(designSpecificationIds);
query = query.Where(x => x.SpecReferences.Any(s => designSpecificationIdsHash.Contains(s.Id)));
}
where designSpecificationIds is an array of integers passed from the web form.
When I run the application and step through the search code, the SQL generated from the query is:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[SSCId] AS [SSCId],
[Extent1].[Name] AS [Name],
[Extent1].[SystemFunction] AS [SystemFunction],
[Extent1].[Remarks] AS [Remarks],
[Extent1].[IsSystem] AS [IsSystem],
[Extent1].[IsGrouping] AS [IsGrouping],
[Extent1].[IsConfiguration] AS [IsConfiguration],
[Extent1].[FieldTag] AS [FieldTag],
[Extent1].[ParentId] AS [ParentId],
[Extent1].[IPlantParentId] AS [IPlantParentId],
[Extent1].[BuildingId] AS [BuildingId],
[Extent1].[IsOperable] AS [IsOperable],
[Extent1].[IsAvailable] AS [IsAvailable],
[Extent1].[DutyAreaId] AS [DutyAreaId],
[Extent1].[IsDeleted] AS [IsDeleted]
FROM [dbo].[SSCs] AS [Extent1]
WHERE ([Extent1].[IsDeleted] <> 1) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SpecReferenceSSCs] AS [Extent2]
WHERE ([Extent1].[Id] = [Extent2].[SSC_Id]) AND ([Extent2].[SpecReference_Id] IN (4))
))
If I run this SQL against the database, I get the one result that I expect. However, when I examine the Linq query and attempt to enumerate the results, I get "Enumeration yielded no results." How is it possible that the SQL generated by a Linq query is returning different results than the query itself?
Edit:
To return the values, I'm using ExpressMapper to convert the Model to a DTO. The conversion is simply:
return Mapper.Map<IEnumerable<SSC>, IEnumerable<SSCIndexDto>>(query);
The DTO is:
public class SSCIndexDto {
public int Id { get; set; }
public string Name { get; set; }
//Other properties omitted for brevity
public List<SpecReferenceIndexDto> SpecReferences { get; set; }
}
Again, the mapping appears to be working correctly for all other properties, including navigation properties that are written exactly like SpecReferences. It's the query itself that's not returning any data.
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.
I have a fairly simple (code first) model:
Employee
[Table("vEmployee")] //note v - it's a view
public class Employee
{
[Key]
public int EmployeeNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
EmployeeHolidayEntitlement
[Table("tblEmployeeHolidayEntitlement")]
public class EmployeeHolidayEntitlement
{
[Key]
public int EmployeeNumber { get; set; }
public virtual Employee Employee { get; set; }
public decimal StandardEntitlement { get; set; }
//.....omitted for brevity
}
Note that EmployeeHolidayEntitlement is mapped to a table, and Employee is mapped to a view
When building my context, I do:
(not sure if this is correct!)
modelBuilder.Entity<Employee>()
.HasOptional(x => x.HolidayEntitlement)
.WithRequired(x => x.Employee);
Now, when I query, like this:
var db = new ApiContext();
var result = db.Employees.ToList();
It's very slow.
If I look in SQL profiler, I can see that instead of one statement (joining vEmployee and tblEmployeeHolidayEntitlement) I get many statements executed (one per Employee record) - for example:
First, it selects from vEmployee
SELECT
[Extent1].[id] AS [EmployeeNumber],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName],
FROM [dbo].[vEmployee] AS [Extent1]
then one of these for each record returned
exec sp_executesql N'SELECT
[Extent1].[EmployeeNumber] AS [EmployeeNumber],
[Extent1].[StandardEntitlement] AS [StandardEntitlement]
FROM [dbo].[tblEmployeeHolidayEntitlement] AS [Extent1]
WHERE [Extent1].[EmployeeNumber] = #EntityKeyValue1',N'#EntityKeyValue1 int',#EntityKeyValue1=175219
This doesn't seem right to me -
I would of thought it should be doing something more along the lines of a LEFT JOIN like
SELECT *
FROM [dbo].[vEmployee] employee
LEFT JOIN
[dbo].[tblEmployeeHolidayEntitlement employeeEntitlement
ON
employee.id = employeeEntitlement.employeenumber
You have to use the Include method, like db.Employees.Include(e => e.HolidayEntitlement).ToList(). If you don't and you access the property you'll trigger lazy loading. That's what's happening to you.
For more information check the documentation on loading. The short of it is that if it always joined your entire object graph it'd be unacceptably slow.
This really make me crazy and i cant figure out why hope someone could give me a little hint why it's behave so. I have 4 tables
1st group of these 2 tables and is able to give me a clean and nice T-SQL (sample from this link)
public class Standard
{
public Standard()
{
Students = new List<Student>();
}
public int StandardId { get; set; }
public string StandardName { get; set; }
public string Description { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
public class Student
{
public Student() { }
public int StudentId { get; set; }
public string StudentName { get; set; }
public virtual Standard Standard { get; set; }
}
With Above tables and i use this
LINQ
List<Student> student = context.student.ToList();
var r = from ord in context.student.Include("standard")
select ord;
Ouput
SELECT
[Extent1].[StudentId] AS [StudentId],
[Extent1].[StudentName] AS [StudentName],
[Extent2].[StandardId] AS [StandardId],
[Extent2].[StandardName] AS [StandardName],
[Extent2].[Description] AS [Description]
FROM [dbo].[Students] AS [Extent1]
LEFT OUTER JOIN [dbo].[Standards] AS [Extent2] ON [Extent1].[Standard_StandardId] = [Extent2].[StandardId]
But with 2nd Group
public partial class Cust_ProfileTbl
{
public Cust_ProfileTbl()
{
balance = new List<BP_BalanceTbl>();
}
[Key]
public virtual long bintAccountNo { get; set; }
public string varCardNo { get; set; }
public virtual ICollection<BP_BalanceTbl> balance { get; set; }
}
public class BP_BalanceTbl
{
public BP_BalanceTbl() { }
public virtual long bintAccountNo { get; set; }
[Key]
public int intid { get; set; }
public virtual Cust_ProfileTbl profile { get; set; }
}
with this LINQ
List<Cust_ProfileTbl> profile = context.profile.ToList();
var rs = from ord in context.profile.Include("balance")
select ord;
Output
SELECT
[Project1].[C1] AS [C1],
[Project1].[bintAccountNo] AS [bintAccountNo],
[Project1].[varCardNo] AS [varCardNo],
[Project1].[C2] AS [C2],
[Project1].[intid] AS [intid],
[Project1].[bintAccountNo1] AS [bintAccountNo1]
FROM ( SELECT
[Extent1].[bintAccountNo] AS [bintAccountNo],
[Extent1].[varCardNo] AS [varCardNo],
1 AS [C1], --Why it generate this>?
[Extent2].[intid] AS [intid],
[Extent2].[bintAccountNo] AS [bintAccountNo1],
CASE WHEN ([Extent2].[intid] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2] --Why it generate this>?
FROM [dbo].[Cust_ProfileTbl] AS [Extent1]
LEFT OUTER JOIN [dbo].[BP_BalanceTbl] AS [Extent2] ON [Extent1].[bintAccountNo] = [Extent2].[bintAccountNo]
) AS [Project1]
ORDER BY [Project1].[bintAccountNo] ASC, [Project1].[C2] ASC
Questions
Why in 2nd LINQ it's generate C1?
Why in 2nd LINQ have this line CASE WHEN ([Extent2].[intid] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2] --Why it generate this>?
Why the 2nd output is so complex?
The C1 column doesn't appear to be relevant to the query - it might be an optimisation or protection that happens automatically so that LINQ can't accidentally create something invalid - or perhaps so that the first row can't inadvertently have NULL values.
The C2 column is generated as a protection against a null value since the value is attributed as a Primary Key (which means that it cannot be empty). Since you are doing a LEFT OUTER JOIN, the values on the left may not have joining records - but must still display a valid row for the LEFT information.
The query only looks more complex because it's constructing this extra information in a temporary table before selecting the results. It might be laying the query out in this way just because that's the only way it knows how to generate it through code - or maybe it's smart enough to know that this query is slightly more optimal for a query engine (I probably wouldn't bet on that).
In first case you are doing Include for simple navigation property
(thus it can be done with simple left outer join and each row in response will be materialized as entity in result),
in second case collection is included thus several rows from result should be merged into single entity and its collection property. Thus SQL query have to be written in following way that:
1. All rows to be merged in single entity will be fetched sequentially
2. Facilitate process of detection group bounds
3. Reduce data duplication
Some parts of generated SQL can be eliminated in this simple case, but they are used in more complex queries when several collection properties are included e.t.c.
i'm trying to upgrade an old CMS to use NHibernate and can't deter from the original database structure much. Here is the bit which is causing an issue. Say i have the following 2 tables:
Articles:
- Id (PK, Identity)
- Title
- Content
Meta:
- ArticleId (PK, FK to Articles)
- Description
- Keywords
I have created the following classes:
public class Article {
public virtual int Id { get; set; }
public virtual string Title { get; set; }
public virtual string Content { get; set; }
}
public class Meta : IComponent {
public virtual string Description { get; set; }
public virtual string Keywords { get; set; }
}
public interface IComponent {
}
Usually the Meta would normally be mapped as a component (or a one to one relationship) property on the Article class. However in the application i'm building an admin can enable/disable the components that apply to articles. Also i'd like them to extend the application to add their own components without touching the Article class.
For them reasons i can't add a property against the Article class. Now ideally in my code i'd like to be able to say:
var articles = session.Query<Article>()
.Fetch(a = a.Component<Meta>())
.Where(a => a.Component<Meta>().Keywords.Contains("Some Word"))
.ToList();
// This wouldn't generate an extra SQL statement
var keywords = articles[0].Component<Meta>().Keywords;
Which would generate the following SQL (or similar):
SELECT * FROM Articles INNER JOIN Meta ON Articles.Id = Meta.ArticleId WHERE Meta.Keywords LIKE '%Some Word%'
Is it possible to map the Component method so that it does an inner join to get the Meta. The concept seems pretty simple but i don't have a clue where begin. I'd really appreciate the help.
Thanks
Given this:
public class Article
{
public virtual int ArticleId { get; set; }
public virtual string Title { get; set; }
public virtual string Content { get; set; }
}
public class Meta : IComponent
{
public virtual Article Article { get; set; }
public virtual int MetaId { get; set; }
public virtual string Description { get; set; }
public virtual string Keywords { get; set; }
}
AFAIK, you cannot Fetch something that isn't part of an entity. So from your example, it's not possible to fetch Meta from the Article entity.
So if you want to fetch the other info of an Article, you just have to join Article to them, then project the complete data in your Linq, example:
var articles =
from a in s.Query<Article>()
join m in s.Query<Meta>() on a equals m.Article
where m.Keywords.Contains("Some Word")
select new { a, m };
foreach(var x in articles)
Console.WriteLine("{0} {1}", x.a.Title, x.m.Description);
Resulting query:
select *
from [Article] article0_, [Meta] meta1_
where meta1_.ArticleId = article0_.ArticleId
and meta1_.Keywords like '%Some Word%'
Another approach, start from Meta, then fetch the Article; on query, this will join the Article immediately, i.e. no lazy loading:
var artB =
from m in s.Query<Meta>().Fetch(x => x.Article)
where m.Keywords.Contains("Some Word")
select m;
foreach (var x in artB)
Console.WriteLine("{0} {1}", x.Article.Title, x.Description);
Resulting query:
select *
from [Meta] meta0_
left outer join [Article] article1_ on meta0_.ArticleId = article1_.ArticleId
where meta0_.Keywords like '%Some Word%'
To keep an Article having only one Meta, put a Unique on Meta's reference:
create table Article
(
ArticleId int identity(1,1) not null primary key,
Title varchar(100) not null,
Content varchar(100) not null
);
create table Meta
(
-- this prevents an Article having two Meta
ArticleId int not null references Article(ArticleId) unique,
MetaId int identity(1,1) not null primary key,
Description varchar(100) not null,
Keywords varchar(100) not null
);
insert into Article(Title,Content) values('Great','Yeah')
insert into Meta(ArticleId, Description, Keywords) values(1,'Oh','Some Word');
In your NHibernate mapping file, you can specify the fetch type. The spec for the one-to-one xml is located in the NHibernate Documentation. Notice that number 5 has an option of Join and Select and it defaults to select.
Would the following solution be of interest?
You can make a protected mapping to your components and access these from this public generic method.
This way you can choose to eager/lazy load your components.
public class Article
{
protected virtual ICollection<IComponent> Components { get; set; }
public virtual T Component<T>() where T : IComponent
{
return Components.FirstOrDefault(c=>c.Type==typeof(T));
}
}