We've recently made the switch over to EF Core at our company, and for the last 20+ years we've used stored procedures for our ORM.
I know EF core generates queries that run at the database level rather than enumerating through the data within memory.
I'm not totally sure how to ask this question so let me give you some scenarios!
These code snippets are trimmed down and extracted from a large customer system.
Scenario 1 (the old way):
Take the object 'Membership_InsuranceExtension'. This stores the relationship between a Membership and an InsuranceExtension. Static data regarding the Insurance Extension is stored in another table, named 'tbl_MembershipLevel_InsuranceExtension'
When there is a need to fetch a large list of an object (can be thousands+ of rows returned in the same result set). We create an extra class/object called xxxx_SummaryItem. This contains properties such as StatusName and InsuranceExtensionName which need to be joined... A 'trimmed down' version of the object used purely for display purposes.
This works well, and is pretty fast but there doesn't seem to be a 'native' way of doing this in EF Core without loading the entire 'child object' (unless I am missing something!)
Example stored procedure below:
CREATE PROCEDURE [dbo].[usp_Membership_InsuranceExtension_FetchSummaryList]
@MembershipID uniqueidentifier
AS
BEGIN
SET NOCOUNT ON;
SELECT
tbl_Membership_InsuranceExtension.MembershipInsuranceExtensionID
, tbl_Membership_InsuranceExtension.Created
, tbl_Membership_InsuranceExtension.LastUpdated
, tbl_Membership_InsuranceExtension.MembershipID
, tbl_Membership_InsuranceExtension.StatusID
, tbl_Membership_InsuranceExtension_Status.[Name] AS StatusName
, tbl_Membership_InsuranceExtension.InsuranceExtensionID
, tbl_MembershipLevel_InsuranceExtension.[Name] AS InsuranceExtensionName
, tbl_Membership_InsuranceExtension.ValidFrom
, tbl_Membership_InsuranceExtension.ValidTo
FROM
tbl_Membership_InsuranceExtension
LEFT OUTER JOIN tbl_MembershipLevel_InsuranceExtension ON tbl_Membership_InsuranceExtension.InsuranceExtensionID = tbl_MembershipLevel_InsuranceExtension.InsuranceExtensionID
LEFT OUTER JOIN tbl_Membership_InsuranceExtension_Status ON tbl_Membership_InsuranceExtension.StatusID = tbl_Membership_InsuranceExtension_Status.InsuranceExtensionStatusID
WHERE
tbl_Membership_InsuranceExtension.MembershipID = @MembershipID
ORDER BY
tbl_Membership_InsuranceExtension.Created
Scenario 2 (the EF Core way)
I guess this is less of a scenario and more of a question, but what is the best way to recreate this behaviour within EF without loading the full object (MembershipLevel_InsuranceExtension) in this case for every row returned by the query?
Entities (for example)
BaseEntity
contains the PK, Created/LastUpdated fields
Child entities are currently being eager loaded with the .Include method in my repository.
public class Membership : BaseEntity
{
public Membership() : base()
{
ValidFrom = DateTime.Today.Date;
ValidTo = ValidFrom.AddYears(1).AddDays(-1);
}
public virtual List<Membership_InsuranceExtension> InsuranceExtensions { get; set; }
public DateTime ValidFrom { get; set; }
public DateTime ValidTo { get; set; }
}
public class Membership_InsuranceExtension : BaseEntity
{
public enum InsuranceExtensionStatusType
{
Unknown = 0,
Unverified = 1,
Verified = 2,
Rejected = 3,
Cancelled = 4,
Deleted = 100
}
public Membership_InsuranceExtension(Membership membership) : base()
{
Status = InsuranceExtensionStatusType.Unverified;
Membership = membership;
ValidFrom = membership.ValidFrom;
ValidTo = membership.ValidTo;
}
public Guid MembershipID { get; set; }
public virtual Membership Membership { get; set; }
[Column("StatusID")]
public InsuranceExtensionStatusType Status { get; set; }
public Guid InsuranceExtensionID { get; set; }
public virtual MembershipLevel_InsuranceExtension InsuranceExtension { get; set; }
public DateTime ValidFrom { get; set; }
public DateTime ValidTo { get; set; }
}
public class MembershipLevel_InsuranceExtension : BaseEntity
{
public MembershipLevel_InsuranceExtension() : base()
{
Name = String.Empty;
Description = String.Empty;
}
public string Name { get; set; }
public string Description { get; set; }
}
2条答案
按热度按时间a8jjtwal1#
You can create helper class with extension methods which mimic your current functionality. Note that query can be inaccurate because no all fileds amd vagigations present:
In your code, just call:
It also gives another benefits, you can manipulate with query:
EF Core should generate near optimal query without retrieving full objects from database.
nwlls2ji2#
Short answer, yes. EF can manage scenarios like this quite well. You do not need to explicitly join entities together like you would with SQL.
Instead you set up entities to reflect the tables they will be data drawing from and reference other related entities through what are called navigation properties. These are associated as 1-to-1 or 1-to-many or many-to-many relationships based on the table structure through their respective FKs.
EF does support the explicit
Join
but this is actually intended only to support loose associations where you don't have a dedicated FK (such as OwnerType + OwnerID scenarios to point to a reference in one of two or more tables).Once you have the relationships mapped, selecting just a sub-set of data is an operation called projection which can be done using
Select
or through supporting mappers. For instance Automapper supports this viaProjectTo
. So you can define a DTO or ViewModel with the fields you want cherry picked from the entity and it's related entities, then populate that DTO /wSelect
.This does not involve loading entities into memory, so no eager or lazy loading. EF uses the entity definitions to build an SQL SELECT statement for just the columns you want to consume. It does the heavy lifting that you would have written in a stored procedure or view to fetch a subset of data.
Unfortunately a great many EF examples out there define entities and relations but then don't really cover Projection for read operations, instead loading entire entity graphs into memory. This is honestly only needed when you want to update an entity graph. For read operations projection is the way to go.