Entity Framework: can Include method work with a View? - view

I have a View in my project and I would like to retrieve all data from a table whose Ids is in the View.
Below the script to create a very simplified database (View_1 and Motors and the same but in real case View_1 is more complex):
USE [LacTest]
GO
/****** Object: Table [dbo].[Motors] Script Date: 11/16/2021 4:57:30 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Motors](
[MotorId] [int] IDENTITY(1,1) NOT NULL,
[CompanyId] [int] NOT NULL,
[TotSales1] [int] NOT NULL,
[TotSales2] [int] NOT NULL,
CONSTRAINT [PK_Motors] PRIMARY KEY CLUSTERED
(
[MotorId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[LacCompanies] Script Date: 11/16/2021 4:57:30 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[LacCompanies](
[CompanyId] [int] IDENTITY(1,1) NOT NULL,
[Tot1] [int] NOT NULL,
[Tot2] [int] NOT NULL,
CONSTRAINT [PK_Companies] PRIMARY KEY CLUSTERED
(
[CompanyId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[LacCompanies] ON
GO
INSERT [dbo].[LacCompanies] ([CompanyId], [Tot1], [Tot2]) VALUES (1, 300, 200)
GO
INSERT [dbo].[LacCompanies] ([CompanyId], [Tot1], [Tot2]) VALUES (2, 400, 100)
GO
INSERT [dbo].[LacCompanies] ([CompanyId], [Tot1], [Tot2]) VALUES (3, 500, 100)
GO
INSERT [dbo].[LacCompanies] ([CompanyId], [Tot1], [Tot2]) VALUES (4, 600, 200)
GO
INSERT [dbo].[LacCompanies] ([CompanyId], [Tot1], [Tot2]) VALUES (5, 700, 500)
GO
INSERT [dbo].[LacCompanies] ([CompanyId], [Tot1], [Tot2]) VALUES (6, 800, 400)
GO
INSERT [dbo].[LacCompanies] ([CompanyId], [Tot1], [Tot2]) VALUES (7, 900, 300)
GO
INSERT [dbo].[LacCompanies] ([CompanyId], [Tot1], [Tot2]) VALUES (8, 50, 20)
GO
INSERT [dbo].[LacCompanies] ([CompanyId], [Tot1], [Tot2]) VALUES (9, 80, 20)
GO
INSERT [dbo].[LacCompanies] ([CompanyId], [Tot1], [Tot2]) VALUES (10, 40, 10)
GO
SET IDENTITY_INSERT [dbo].[LacCompanies] OFF
GO
SET IDENTITY_INSERT [dbo].[Motors] ON
GO
INSERT [dbo].[Motors] ([MotorId], [CompanyId], [TotSales1], [TotSales2]) VALUES (1, 4, 35, 23)
GO
INSERT [dbo].[Motors] ([MotorId], [CompanyId], [TotSales1], [TotSales2]) VALUES (2, 5, 140, 70)
GO
INSERT [dbo].[Motors] ([MotorId], [CompanyId], [TotSales1], [TotSales2]) VALUES (3, 7, 200, 24)
GO
INSERT [dbo].[Motors] ([MotorId], [CompanyId], [TotSales1], [TotSales2]) VALUES (4, 9, 2, 1)
GO
SET IDENTITY_INSERT [dbo].[Motors] OFF
GO
ALTER TABLE [dbo].[Motors] WITH CHECK ADD CONSTRAINT [FK_Motors_Companies] FOREIGN KEY([CompanyId])
REFERENCES [dbo].[LacCompanies] ([CompanyId])
GO
ALTER TABLE [dbo].[Motors] CHECK CONSTRAINT [FK_Motors_Companies]
GO
/****** Object: View [dbo].[View_1] Script Date: 11/16/2021 4:57:30 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE VIEW [dbo].[View_1]
AS
SELECT MotorId, CompanyId, TotSales1, TotSales2
FROM dbo.Motors
GO
These are my classes:
public class Motors
{
[Key]
public int MotorId { get; set; }
[ForeignKey("CompanyId")]
public LacCompanies Company { get; set; }
public int TotSales1 { get; set; }
public int TotSales2 { get; set; }
}
public class LacCompanies
{
[Key]
public int CompanyId { get; set; }
public int Tot1 { get; set; }
public int Tot2 { get; set; }
public virtual Motors Motors { get; set; }
}
public class View_1
{
public int MotorId { get; set; }
public int CompanyId { get; set; }
public int TotSales1 { get; set; }
public int TotSales2 { get; set; }
public virtual Motors Motors { get; set; }
}
I would like to retrieve Motors from View_1 by Include method in this way:
View_1.Include(x => x.Motors)
is it possible? Any advice?

I changed my code according to the design pattern described in this post:
public class View_1{
public int MotorId { get; set; }
public int CompanyId { get; set; }
public virtual Motors Motor { get; set; }
public virtual LacCompanies Company { get; set; }
}
modelBuilder.Entity<View_1>(entity => {
entity.HasNoKey();
entity.ToView("View_1");
entity.HasOne(e => e.Company)
.WithOne()
.HasForeignKey<View_1>(e => e.CompanyId);
entity.HasOne(e => e.Motor)
.WithOne()
.HasForeignKey<View_1>(e => e.MotorId);
}
and now I can navigate in both classes.

Related

LINQ Join Group By Select New

I have the following entities:
User is:
public String Id { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
Assessment is:
public int Id { get; set; }
public int SymptomId { get; set; }
public string UserId { get; set; }
public DateTime CreatedDate { get; set; }
A User can have 0, 1 or more Assessments.
I have written the following LINQ:
// Get Paged Users Recent Assessment List:
var _usersWithRecentAssessment =
from U in _context.Users
join A in _context.Assessments on U.Id equals A.UserId
group A by A.UserId into uaGroup
select uaGroup.OrderByDescending(a => a.CreatedDate).FirstOrDefault();
_usersWithRecentAssessment = _usersWithRecentAssessment.OrderByDescending(ua => ua.CreatedDate);
which returns the most recent symptom assessment for all Users that have completed an Assessment (and orders the assessment list in descending order of Assessment CreatedDate) as follows:
[
{
"id": 1052,
"symptomId": 44,
"userId": "b978d113-7da7-4b7f-a121-9dd71e158dd4",
"createdDate": "2019-11-16T12:50:05.2175621"
},
{
"id": 1051,
"symptomId": 44,
"userId": "5230f4b7-bf2a-46b0-88a0-6f13fa5caa91",
"createdDate": "2019-11-03T14:46:21.6598763"
}
]
I would like to return the following AssessmentDTO
where AssessmentDTO is:
public int Id { get; set; }
public int SymptomId { get; set; }
public string UserId { get; set; }
public string UserFirstName { get; set; }
public string UserLastName { get; set; }
public DateTime CreatedDate { get; set; }
which contains the additional attributes UserId, UserFirstName, and UserLastName from the User entity.
I have tried unsuccessfully to add 'select new AssessmentDTO() { }' at the end of the LINQ.
Can someone please help me?
A User has zero or more Assessments, every Assessment belongs to exactly one User. This is a standard one-to-many relation with a foreign key.
For your problem, you can use one of the overload of Queryable.GroupBy. First get all Users with their assessments and as a result keep only the most recent assessment:
var UsersWithMostRecentAssessment = dbContext.Users
.GroupBy(dbContext.Assessments, // GroupJoin Users and Assessments
user => user.Id, // From every User take the Id
assessment => assessment.UserId, // From every Assessment take the UserId
// ResultSelector: take each User with its zero or more Assessments to make one new:
(user, assessmentsOfThisUser) => new
{
User = user,
MostRecentAssessment = assessmentsOfThisUser
.OrderByDescending(assessment => assessment.CreatedDate)
.FirstOrDefault(),
// might be null if this User has not assessments at all
})
// Now get the User with its MostRecentAssessment (or null) to make one new:
.Select(userAssessMent => new
{
// Values from the most recent assessment:
Id = userAssessment.MostRecentAssessment.Id,
SymptomId = userAssessment.MostRecentAssessment.SymptomId,
...
// Values from the User:
UserId = userAssessment.User.Id,
FirstName = userAssessment.User.FirstName,
LastName = userAssessment.User.LastName,
...
})
Note: this will go wrong if there are users without assessments, because they won't have a most recent assessment. You can omit these users before the final Select:
.Where(userAssessment => userAssessment.MostRecentAssessment != null)
Or if you want these Users in your endresult:
// Values from the most recent assessment or default if null
Id = userAssessment.MostRecentAssessment.Id ?? 0,
SymptomId = userAssessment.MostRecentAssessment.SymptomId ?? 0,

System.Data.SqlClient.SqlException (0x80131904): Invalid column name 'PayasYouGo'

I am currently following Mosh Hamedami tutorial on Seeding the Database using code first migration. Unfortunately I came with this error System.Data.SqlClient.SqlException (0x80131904): Invalid column name 'PayasYouGo'. I have tried re-creating my model but nothing seems to work.
The Model:
public class MembershipType
{
public byte Id { get; set; }
public short SignUpFee { get; set; }
public byte DurationInMonths { get; set; }
public byte DiscountRate { get; set; }
public string MembershipName { get; set; }
}
The Migration Code
public partial class PopulateMembership : DbMigration
{
public override void Up()
{
Sql("INSERT INTO MembershipTypes (Id, SignUpFee, DurationInMonths, DiscountRate, MembershipName) VALUES (1, 0, 0, 0, PayasYouGo)");
Sql("INSERT INTO MembershipTypes (Id, SignUpFee, DurationInMonths, DiscountRate, MembershipName) VALUES (2, 30, 1, 10, Monthly)");
Sql("INSERT INTO MembershipTypes (Id, SignUpFee, DurationInMonths, DiscountRate, MembershipName) VALUES (3, 90, 3, 15, Quarterly)");
Sql("INSERT INTO MembershipTypes (Id, SignUpFee, DurationInMonths, DiscountRate, MembershipName) VALUES (4, 300, 12, 20, Annual)");
}
public override void Down()
{
}
}
The Database Migrated
What am I doing wrong here? What is causing this error? What does this error mean? Thanks in advance
You wanted to supply a string but instead gave SQL Server a name.
String literals in SQL Server are surrounded by single quote characters:
Sql(#"INSERT INTO MembershipTypes
(Id, SignUpFee, DurationInMonths, DiscountRate, MembershipName)
VALUES (1, 0, 0, 0, 'PayasYouGo')");
(If it had worked as you expected, consider how you would have struggled if you wanted to insert a string that contained commas, brackets, white space, etc)

Entity Framework 6 TPH Inheritance Enum Field Generate Slow Query

I use EF 6.0 code first and TPH inheritance in my project. I see that if i use enum field in my classes EF generates sql statement with casting. So my query runs very slowly and performance is down. How can i remove casting in my sql query?
Thanks.
var db = new AppContext();
var p = db.Products.FirstOrDefault(x => x.ProdId == 1);
public enum MyEnum
{
Field1 = 1,
Field2 = 2
}
public class Product
{
[Key]
public int ProdId { get; set; }
public string ProdName { get; set; }
//I run my code with this property and without this property.
//public MyEnum MyEnum { get; set; }
}
public class Chair : Product
{
public string ChairProp1 { get; set; }
}
public class Seat : Product
{
public string SeatProp1 { get; set; }
}
//EF generate this SQL without enum field. This is good SQL statements.
SELECT TOP (1)
[Extent1].[Discriminator] AS [Discriminator],
[Extent1].[ProdId] AS [ProdId],
[Extent1].[ProdName] AS [ProdName],
[Extent1].[ChairProp1] AS [ChairProp1],
[Extent1].[SeatProp1] AS [SeatProp1]
FROM [dbo].[Products] AS [Extent1]
WHERE ([Extent1].[Discriminator] IN (N'Chair',N'Seat',N'Product')) AND (1 = [Extent1].[ProdId])
//EF generate this SQL with enum field (myenum). there is alot of casting.i want to remove casting.
SELECT
[Limit1].[C1] AS [C1],
[Limit1].[ProdId] AS [ProdId],
[Limit1].[ProdName] AS [ProdName],
[Limit1].[MyEnum] AS [MyEnum],
[Limit1].[C2] AS [C2],
[Limit1].[C3] AS [C3]
FROM ( SELECT TOP (1)
[Extent1].[ProdId] AS [ProdId],
[Extent1].[ProdName] AS [ProdName],
[Extent1].[MyEnum] AS [MyEnum],
CASE WHEN ([Extent1].[Discriminator] = N'Product') THEN '0X' WHEN ([Extent1].[Discriminator] = N'Chair') THEN '0X0X' ELSE '0X1X' END AS [C1],
CASE WHEN ([Extent1].[Discriminator] = N'Product') THEN CAST(NULL AS varchar(1)) WHEN ([Extent1].[Discriminator] = N'Chair') THEN [Extent1].[ChairProp1] END AS [C2],
CASE WHEN ([Extent1].[Discriminator] = N'Product') THEN CAST(NULL AS varchar(1)) WHEN ([Extent1].[Discriminator] = N'Chair') THEN CAST(NULL AS varchar(1)) ELSE [Extent1].[SeatProp1] END AS [C3]
FROM [dbo].[Products] AS [Extent1]
WHERE ([Extent1].[Discriminator] IN (N'Chair',N'Seat',N'Product')) AND (1 = [Extent1].[ProdId])
) AS [Limit1]
It seems you missed the configuration of discriminator . You need to specify which field is the discriminator and what's the type of that ,
You can do it by fluent api like this :
public class ChairConfig : EntityTypeConfiguration<Chair>
{
public ChairConfig()
{
this.Map(m => m.Requires(discriminator: "MyEnum").HasValue(MyEnum.Field1));
}
}
public class SeatConfig : EntityTypeConfiguration<Seat>
{
public SeatConfig()
{
this.Map(m => m.Requires(discriminator: "MyEnum").HasValue(MyEnum.Field2));
}
}
And in query you need to specify which type you wanna retrieve like this :
var db = new AppContext();
var p = db.Products.OfType<Chair>().FirstOrDefault(x => x.ProdId == 1);

select and update based on a child objects property minimum value using Linq

I have a Type Supplier that has a property SupplierId and a another property NearestLocation which is a of Type SupplierLocation, the SupplierLocation consists of properties SupplierId and DistanceFromDevice
class Supplier
{
public int SupplierId { get; set; }
public SupplierLocation NearestLocation { get; set; }
}
class SupplierLocation
{
public int SupplierId { get; set; }
public decimal DistanceFromDevice { get; set; }
public double Longitude { get; set; }
public double Latitude {get; set;}
}
I have a List of all my Supplierlocations a supplier can have a n number of locations. I have also calculated the DistanceFromDevice property for each location.
I have a List whose id's can be found in the SupplierLocations List.
What I would like to do using linq is to join my supplier to the SupplierLocation by the SupplierId and populate the NearestLocation Property of the Supplier class with the the Location that has the least DistanceFromDevice value of all the locations for that particular supplier.
Hope this makes sense. Can this be done using linq.
Many thanks in advance.
Paul
Here is a working example in LINQPad
void Main()
{
var suppliers = new List<Supplier>
{
new Supplier() {SupplierId = 1},
new Supplier() {SupplierId = 2},
new Supplier() {SupplierId = 5}
};
var locations = new List<SupplierLocation>
{
new SupplierLocation {SupplierId = 1, DistanceFromDevice = 10, Latitude = 1, Longitude = 2},
new SupplierLocation {SupplierId = 1, DistanceFromDevice = 20, Latitude = 1, Longitude = 3},
new SupplierLocation {SupplierId = 1, DistanceFromDevice = 30, Latitude = 1, Longitude = 4},
new SupplierLocation {SupplierId = 1, DistanceFromDevice = 40, Latitude = 1, Longitude = 5},
new SupplierLocation {SupplierId = 2, DistanceFromDevice = 10, Latitude = 2, Longitude = 2},
new SupplierLocation {SupplierId = 2, DistanceFromDevice = 20, Latitude = 2, Longitude = 3},
new SupplierLocation {SupplierId = 3, DistanceFromDevice = 10, Latitude = 3, Longitude = 2}
};
var result = from s in suppliers
join l in locations on s.SupplierId equals l.SupplierId
into grp
where grp.Count() > 0
select new Supplier() { SupplierId = s.SupplierId, NearestLocation = grp.OrderBy (g => g.DistanceFromDevice).First()};
result.Dump();
}
class Supplier
{
public int SupplierId { get; set; }
public SupplierLocation NearestLocation{ get; set; }
}
class SupplierLocation
{
public int SupplierId { get ;set; }
public decimal DistanceFromDevice { get; set; }
public double Longitude { get; set; }
public double Latitude {get; set;}
}
So, you want to set NearestLocation on Supplier where the SupplierId is equal to one in List<SupplierLocation>?
Assume you have a List<SupplierLocation> named "Locations" and "currentSupplier" is the Supplier you want to assign the NearestLocation of:
try
{
var minForSupplier = Locations.Where(x => x.SupplierId == currentSupplier.SupplierId).Min(x => x.DistanceFromDevice);
currentSupplier.NearestLocation = Locations.Where(x => x.SupplierId == currentSupplier.SupplierId && x.DistanceFromDevice == minForSupplier).Single();
}
catch(InvalidOperationException e)
{
// There is more than one SupplierLocation
}

Geting an error when executin the stored Procedure in my MVC3?

Hi all i have method where i pass some parameters to the stored procedure and when i execute it i get this error
[The specified cast from a materialized 'System.Int32' type to the 'System.String' type is not valid.]
this is how i am calling my storedprocedure
public JsonResult CreatedBugs()
{
int year;
int month;
int projectid;
year = 2012;
month = 8;
projectid = 16;
var loggedbugs = db.ExecuteStoreQuery<LoggedBugs>("LoggedBugs #Year,#Month,#ProjectID", new SqlParameter("#Year",year ), new SqlParameter("#Month", month ), new SqlParameter("#ProjectID", projectid )).ToList();
var ClosedBugs = db.ExecuteStoreQuery<LoggedBugs>("ClosedBugs #Year,#Month,#ProjectID", new SqlParameter("#Year", year), new SqlParameter("#Month", month), new SqlParameter("#ProjectID", projectid)).ToList();
var model = new LoggedBugs
{
LoggedBugsCount = loggedbugs,
ClosedBugs = ClosedBugs
};
return Json(model, JsonRequestBehavior.AllowGet);
}
and this is my StoredProcedure
alter procedure LoggedBugs
(
#Year int,
#Month int,
#ProjectID int
)
as
begin
SELECT projectName,
ProjectYear,
ProjectMonth,
Week1, Week2, Week3, Week4, Week5
FROM (
Select p.projectName,YEAR(b.CreatedDate) ProjectYear, MONTH(b.CreatedDate) ProjectMonth,
'Week' + CAST(DATEDIFF(week, DATEADD(MONTH, DATEDIFF(MONTH, 0, b.CreatedDate), 0), b.CreatedDate)+1 AS VARCHAR) as [Weeks],
b.BUGID
From BUGS b inner join projects p on b.ProjectId = p.ProjectId
Where DatePart(year, CreatedDate) = #Year and datepart(month, Createddate) = #Month and b.projectid = #ProjectID
) p
PIVOT (COUNT(BUGID) FOR [Weeks] IN (WEEK1, WEEK2, WEEK3, Week4, Week5)) AS pvt
end
where am i doing wrong here
Edit1
here is my LoggedBug Class
public class LoggedBugs
{
public string projectName { get; set; }
public string ProjectYear { get; set; }
public string ProjectMonth { get; set; }
public string Week1 { get; set; }
public string Week2 { get; set; }
public string Week3 { get; set; }
public string Week4 { get; set; }
public string Week5 { get; set; }
public IEnumerable<LoggedBugs> LoggedBugsCount { get; set; }
public IEnumerable<LoggedBugs> ClosedBugs { get; set; }
}
It can be problem with ProjectYear and ProjectMonth. At database side they are int, so at .NET side they would propably unboxed as System.Int32. In your entity they are string.
Try convert it to varchar in your sp or change types in entity to int.
Change your ProjectYear and ProjectMonth properties to type int in LoggedBugs class. Your sql returns them as int, see YEAR SQL MSDN description since YEAR and MONTH sql functions both return values of type int. Your C# code expects a string, so you get a type mismatch exception.

Resources