Entity Framework - Custom Field that get data from another Table - c#

i have this scenario:
public class Source
{
public int ID { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public virtual ICollection<InvoiceMembership> InvoiceMemberships { get; set;}
}
public class InvoiceMembership
{
public int ID { get; set; }
[Column(TypeName = "date")]
public DateTime StartDate { get; set; }
[Column(TypeName = "date")]
public DateTime? EndDate { get; set; }
public virtual Source source { get; set; }
public virtual InvoiceTemplate InvoiceTemplate { get; set; }
}
public class InvoiceTemplate
{
public int ID { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public bool Enabled { get; set; }
public int NumberOfPayment { get; set; }
}
How can i have a field named CurrentTemplate with type of InvoiceTemplate in Source Entity That have EndDate=null in related InvoiceMembership row?
EDIT:
i use bellow code, but it's not really true way!
[NotMapped]
public InvoiceTemplate CurrentTemplate { get {
var context=new MedicalContext();
var template = context.InvoiceMemberships.Where(m => m.source == this).Where(m => m.EndDate == null).Select(m => m.InvoiceTemplate);
if (template != null)
return (InvoiceTemplate)template;
else
return null;
} }

Yes you can, but with EF computed properties are such a hassle.
Let's say you have:
public class Source
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[NotMapped]
public InvoiceTemplate CurrentTemplate
{
get
{
return InvoiceMemberships
.Where(i = i.EndDate == null)
.Select(i => i.InvoiceTemplate)
.FirstOrDefault();
}
}
public virtual ICollection<InvoiceMembership> InvoiceMemberships { get; set;}
}
There just too many conditions (in my opinion) that should be met to make this work:
CurrentTemplate can't be used directly in a LINQ query: EF will complain that the expression can't be translated into SQL.
You always have to Include() InvoiceMemberships.InvoiceTemplate to be able to access CurrentTemplate in memory, after the context is disposed. This will always load all InvoiceMemberships + InvoiceTemplate for each Source object (in one query).
Without using Include() you can only access CurrentTemplate before the context is disposed. This will trigger lazy loading of all InvoiceMemberships + InvoiceTemplate for each Source object in separate queries.
As you see, you'll always load (much) more data than needed to get only one InvoiceMembership per Source.
The most efficient way is to query the required data into a projection, so the predicate EndDate == null can be included in the SQL query.
We'll have to wait for NHibernate-style formula properties.

Expose your Foreign Key value directly in your InvoiceMembership POCO (it must be there in the database anyway if the relation exists), and the entire query will be elegantly converted to SQL directly by L2E:
public class InvoiceMembership
{
public int ID { get; set; }
public int SourceId { get; set; }
[ForeignKey("SourceId")]
public virtual Source Source { get; set; }
public virtual InvoiceTemplate InvoiceTemplate { get; set; }
}
And in Source:
[NotMapped]
public InvoiceTemplate CurrentTemplate
{
get
{
using (var context = new MedicalContext())
return context.InvoiceMemberships
.Where(m => m.SourceId == this.ID)
.Where(m => m.EndDate == null)
.Select(m => m.InvoiceTemplate)
.FirstOrDefault();
}
}
However, this has the drawback that - each time the property is accessed - the database will be queried. It might be best to move this method to the InvoiceMembership class, where you know your InvoiceMembership objects are getting loaded anyway, and make it a method:
public class InvoiceMembership
{
public int ID { get; set; }
public int SourceId { get; set; }
[ForeignKey("SourceId")]
public virtual Source Source { get; set; }
public virtual InvoiceTemplate InvoiceTemplate { get; set; }
static public InvoiceTemplate ReadCurrentTemplate(int sourceId)
{
using (var context = new MedicalContext())
return context.InvoiceMemberships
.Where(m => m.SourceId == sourceId)
.Where(m => m.EndDate == null)
.Select(m => m.InvoiceTemplate)
.FirstOrDefault();
}
}
So, now, you have a method, not a property, to no longer hide the fact that an action is being taken each time you access it ... the name ReadCurrentTemplate tells you that. And why not make it static now? Furthermore, making it static means you no longer have to worry about NotMappedAttribute.
But we'd still like to have access in Source, wouldn't we (especially if dropping the ICollection<InvoiceMembership> navigation property, as I see in your comments you wish to do)? This is now no longer an EF concern at all, but a regular, lazy-loading (if you wish it to be), concern in the Source class:
readonly Lazy<InvoiceTemplate> _currentTemplate;
public Source()
{
_currentTemplate = new Lazy<InvoiceTemplate>(t => t = InvoiceMembership.ReadCurrentTemplate(ID));
}
[NotMapped]
public InvoiceTemplate CurrentTemplate
{
get { return _currentTemplate.Value; }
}
So, doing this, you're still going to take the hit of running the database query to get the value of CurrentTemplate, but only once to evaluate the Lazy private backer; afterward, for the life of this Source object, this property will be the same as the first time it was read. This seemed to fit the model of how you were going to use it, considering it was only ever a read-only property in your examples. And it would only be for the life of the Source object, which should be in a context anyway.
If not, this should be method ReadCurrentTemplate (non-static, no parameter) on Source as well, and simply return InvoiceMembership.ReadCurrentTemplate(ID) directly, to indicate that each time it's called it's reading from the Database.

Related

How, in EF core, to check if a navigation property is set the most efficient way

I have the following model
public class Asset
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<AssetMetric> Metrics { get; set; }
}
public class AssetMetric
{
public int Id { get; set; }
public string Whatever { get; set; }
public int AssetId { get; set; }
[InverseProperty("Metrics")]
public Asset Asset { get; set; }
}
I want to load a list of assets and check at the same time if some AssetMetric are set for each of them.
I could to something like that
var summary = await _dbContext.Assets.Include(x=>x.Metrics).Select(x=>new AssetSummary()
{
Name = x.Name,
HasMetrics = x.Metrics.Any(),
}).ToListAsync();
However, that mean that I will load all children entities just to check if they exist. I was wondering if a more efficient way to do that exist?
Actually you have written effective query. Include after Select will be ignored and only needed data should be returned. The same query without Include:
var summary = await _dbContext.Assets
.Select(x => new AssetSummary()
{
Name = x.Name,
HasMetrics = x.Metrics.Any(),
}).ToListAsync();
If you are worrying about performance, check generated SQL.

How to load specific objects in entity framework

I have an auto generated class (Orders class) which references other objects. What i'm trying to accomplish is get
an order using the order id and the user related to that order.
However when loading the object it is taking 2 seconds to load it. I looked at the object and EF is populating the list
of all the dependent objects (Language, country, customer user).
How do i get only specific object like only the order properties and the user object ? I tried to accomplish that using
the below code but it is loading all the objects.
//Code for getting order id and user related to it
using (var _storeEntities = new StoreEntities())
{
var test = _storeEntities.Orders.Where(r => r.Id.ToString() == orderId).Include(x => x.User).ToList();
}
// Parent entity
public partial class StoreEntities: DbContext
{
public virtual DbSet<Orders> Orders { get; set; }
}
// Order object
public partial class Orders
{
public System.Guid Id { get; set; }
public bool IsActive { get; set; }
public System.DateTime OrderDate{ get; set; }
public Nullable<int> CustomerId { get; set; }
public string LanguageId { get; set; }
public Nullable<System.Guid> UserId { get; set; }
public string CountryId { get; set; }
public virtual Customer Customer{ get; set; }
public virtual User User{ get; set; }
public virtual Country Country { get; set; }
public virtual Language Language { get; set; }
}
You should disable Lazy loading in configuration
public class YourContext : DbContext
{
public YourContext ()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
Or only during your call
using (var _storeEntities = new StoreEntities())
{
_storeEntities .Configuration.LazyLoadingEnabled = false;
var test = _storeEntities.Orders.Where(r => r.Id.ToString() == orderId)
.Include(x => x.User).ToList();
}
Here is the MSDN article for every type of loading
https://msdn.microsoft.com/en-nz/data/jj574232.aspx
Disable lazy loading:
_storeEntities.Configuration.LazyLoadingEnabled = false;
In constructor or before the query you want to run.
Or just remove virtual keyword.
Also you can disable proxy creation which is used for lazy loading:
_storeEntities.Configuration.ProxyCreationEnabled = false;

Why is my code doing lazy loading even after I turned it off at every possible point?

I would like to get Exams and Test entities that have a UserTest entity with a UserId that is either equal to "0" or to a provided value. I had a number of suggestions but so far none have worked. One suggestion was to start by getting UserTest data and the other solution was to start by getting Exam data. Here's what I have when I used the UserTests as the source starting point.
I have the following LINQ:
var userTests = _uow.UserTests
.GetAll()
.Include(t => t.Test)
.Include(t => t.Test.Exam)
.Where(t => t.UserId == "0" || t.UserId == userId)
.ToList();
When I check _uow.UserTests with the debugger it's a repository and when I check the dbcontext's configuration.lazyloading then it is set to false.
Here's my classes:
public class Exam
{
public int ExamId { get; set; }
public int SubjectId { get; set; }
public string Name { get; set; }
public virtual ICollection<Test> Tests { get; set; }
}
public class Test
{
public int TestId { get; set; }
public int ExamId { get; set; }
public string Title { get; set; }
public virtual ICollection<UserTest> UserTests { get; set; }
}
public class UserTest
{
public int UserTestId { get; set; }
public string UserId { get; set; }
public int TestId { get; set; }
public int QuestionsCount { get; set; }
}
When I looked at the output I saw something like this:
[{"userTestId":2,
"userId":"0",
"testId":12,
"test":{
"testId":12,"examId":1,
"exam":{
"examId":1,"subjectId":1,
"tests":[
{"testId":13,"examId":1,"title":"Sample Test1",
"userTests":[
{"userTestId":3,
"userId":"0",
Note that it gets a UserTest object, then gets a test object and then an exam object. However the exam object contains a test collection and then it heads back down again and gets the different tests and the unit tests inside of those:
UserTest > Test > Exam > Test > UserTest ?
I have tried hard to ensure lazy loading is off and debug tell me it's set to false. I am using EF6 and WebAPI but not sure if that makes a difference as I am debugging at the C# level.
You can't avoid that the inverse navigation properties are populated by EF, no matter if you load related entities with eager or lazy loading. This relationship fixup (as already explained by #Colin) is a feature you can't turn off.
You could solve the problem by nullifying the unwished inverse navigation properties after the query is finished:
foreach (var userTest in userTests)
{
if (userTest.Test != null)
{
userTest.Test.UserTests = null;
if (userTest.Test.Exam != null)
{
userTest.Test.Exam.Tests = null;
}
}
}
However, in my opinion the flaw of your design is that you try to serialize entities instead of data transfer objects ("DTOs") that are specialized to the view where you want to send the data to. By using DTOs you can avoid the inverse navigation properties that you don't want altogether and maybe other entity properties that you don't need in your view. You would define three DTO classes, for example:
public class ExamDTO
{
public int ExamId { get; set; }
public int SubjectId { get; set; }
public string Name { get; set; }
// no Tests collection here
}
public class TestDTO
{
public int TestId { get; set; }
public string Title { get; set; }
// no UserTests collection here
public ExamDTO Exam { get; set; }
}
public class UserTestDTO
{
public int UserTestId { get; set; }
public string UserId { get; set; }
public int QuestionsCount { get; set; }
public TestDTO Test { get; set; }
}
And then use a projection to load the data:
var userTests = _uow.UserTests
.GetAll()
.Where(ut => ut.UserId == "0" || ut.UserId == userId)
.Select(ut => new UserTestDTO
{
UserTestId = ut.UserTestId,
UserId = ut.UserId,
QuestionsCount = ut.QuestionsCount,
Test = new TestDTO
{
TestId = ut.Test.TestId,
Title = ut.Test.Title,
Exam = new ExamDTO
{
ExamId = ut.Test.Exam.ExamId,
SubjectId = ut.Test.Exam.SubjectId,
Name = ut.Test.Exam.Name
}
}
})
.ToList();
You could also "flatten" the object graph by defining only a single DTO class that contains all the properties you need for the view:
public class UserTestDTO
{
public int UserTestId { get; set; }
public string UserId { get; set; }
public int QuestionsCount { get; set; }
public int TestId { get; set; }
public string TestTitle { get; set; }
public int ExamId { get; set; }
public int ExamSubjectId { get; set; }
public string ExamName { get; set; }
}
The projection would become simpler and look like this:
var userTests = _uow.UserTests
.GetAll()
.Where(ut => ut.UserId == "0" || ut.UserId == userId)
.Select(ut => new UserTestDTO
{
UserTestId = ut.UserTestId,
UserId = ut.UserId,
QuestionsCount = ut.QuestionsCount,
TestId = ut.Test.TestId,
TestTitle = ut.Test.Title,
ExamId = ut.Test.Exam.ExamId,
ExamSubjectId = ut.Test.Exam.SubjectId,
ExamName = ut.Test.Exam.Name
})
.ToList();
By using DTOs you do not only avoid the problems of inverse navigation properties but also follow good security practices to "white-list" the exposed property values from your database explicitly. Imagine you would add a test access Password property to the Test entity. With your code that serializes eagerly loaded full entities with all properties the password would get serialized as well and run over the wire. You don't have to change any code for this to happen and in the worst case you wouldn't be aware that you are exposing passwords in a HTTP request. On the other hand when you are defining DTOs a new entity property would only be serialized with your Json data if you add this property explicitly to the DTO class.
Your query will load all UserTests into the context where UserId == "0" || UserId == userId and you have eagerly loaded the related Test and its related Exams.
Now in the debugger you can see that the Exams are linked to some Tests in memory and are assuming that is because they have been lazy-loaded. Not true. They are in memory because you loaded all UserTests into the context where UserId == "0" || UserId == userId and you have eagerly loaded the related Test. And they are linked to the navigation property because EF performs a "fix-up" based on foreign keys.
The Exam.Tests navigation property will contain any entities loaded into the context with the correct foreign key, but will not necessarily contain all Tests linked to the Exam in the database unless you eagerly load it or turn on lazy loading
I believe that deferred execution causes nothing to happen unless something is actually read from userTests. Try to include var userTestsAsList = userTests.ToList() and check with the debugger if userTestsAsList contains the desired sequence.
As far as I can read your POCO-Relationships and your query, your Repo is returning what you asked for. But did you know you asked for this?
You are navigating from Grand-Child <UserTest> to Child <Test> to Parent <Exam>
Your Entity of <Exam> is being treated as a Grand-Child when it seems to be a Grand-Parent (in fact, a graph root) having children of <Test> who have children / grand-children of type <UserTest>.
As you are eager loading (and serializing?), of course your <Exam> should eager load its <Test> Collection, which should load their <UserTest> Collections.
By working your way down the graph, you are causing a full circle.
Did you mean to have the opposite relationships?
public class Exam
{
public int ExamId { get; set; }
public int TestId { get; set; }
public int SubjectId { get; set; }
public string Name { get; set; }
}
public class Test
{
public int TestId { get; set; }
public int ExamId { get; set; }
public string Title { get; set; }
public virtual ICollection<UserTest> UserTests { get; set; }
}
public class UserTest
{
public int UserTestId { get; set; }
public string UserId { get; set; }
public int TestId { get; set; }
public int QuestionsCount { get; set; }
public virtual ICollection<Exam> Exams { get; set; }
}
I'm making many assumptions about your data. This relationships simply makes more sense as real-world entities and by your usage. That Users have Tests and Exams rather than the reverse. If so, this relationship should work with your linq query.
If you try something like:
var userTest = _uow.UserTests
.GetAll()
.Where(t => t.UserId == "0" || t.UserId == userId)
.First();
var name = userTest.Test.Title;
Would your code throw an exception because the Test property hasn't been loaded? I suspect the problem is your repository is using LINQ to SQL and not LINQ to Entities. You can't turn off Lazy Loading with LINQ to SQL. You would have to show how your repository works to fix the problem in that case.
Is there any reson you are using "virtual" for your collections? If you're using "include", I would recommend getting rid of the "virtual"

Reflection To Update Source Entity with Children To Target With Children

For all you Reflection experts I need some help. I am working on a gneric method to be able to use hierarchical data assignments in an MVC application using CodeFirst and EF5. A user would apply changes to the parent object (source) and this method would populate the child object(s) (target). It's not working as I would expect. Any pointers would be greatly appreciated.
The below method "CopyEntity" is intended to take a source entity that has child entities and update a target entity of the same type and structure. We are only looking to update those entities that inherit a base type of 'Association' and also do not have a 'NoInherit' attribute on the members. Not every property would be updated from the 'Association'
public class ClassCode : Inheritable<ClassCode>
{
// Base Properties ----------------------------------
public int LobId { get; set; }
public virtual LOB LOB { get; set; }
public bool IsVirtual { get; set; }
//public string Name { get; set; }
public string Description { get; set; }
[Required(ErrorMessage = "Select Code")]
public string Code { get; set; }
//enable to force recreation of the database
public string IID { get; set; }
public virtual ICollection<ClassSubjectivityAssoc> Subjectivities{ get; set; }
}
public class ClassSubjectivityAssoc : Association
{
[Column("SubjectivityId")]
public override int AssociationId { get; set; }
[ForeignKey("AssociationId")]
public virtual Subjectivity Subjectivity { get; set; }
public int ClassCodeId { get; set; }
[ForeignKey("ClassCodeId")]
public virtual ClassCode ClassCode { get; set; }
}
public abstract class Association : BaseEntity
{
public DateTime Start { get; set; }
public DateTime? End { get; set; }
public bool IsCurrent { get; set; }
public int Order { get; set; }
public bool BreakInheritance { get; set; }
public int? InhId { get; set; }
public virtual ClassCode InhClass { get; set; }
public int CodeId { get; set; }
[ForeignKey("ClassCodeId")]
public virtual Code ClassCode { get; set; }
public abstract int AssociationId {get; set;}
}
public class BaseEntity
{
private static DateTime CREATE_DATE
{
get { return DateTime.Now; }
}
[NoInherit]
public int Id { get; set; }
[NoInherit]
public string CreatedBy { get; set; }
[NoInherit]
public DateTime Created { get; set; }
[NoInherit]
public string LastModifiedBy { get; set; }
[NoInherit]
public DateTime LastModified { get; set; }
public bool IsActive { get; set; }
}
protected void CopyEntity(Inheritable<T> source, Inheritable<T> target)
{
Type sourceType = source.GetType();
Type targetType = target.GetType();
// ensure that the types can be assigned to one another
//if (!targetType.IsAssignableFrom(sourceType)) return;
// loop through the properties of the source system
foreach (PropertyInfo sourceMember in sourceType.GetProperties().Where(sourceMember => sourceMember.GetCustomAttribute(typeof(NoInheritAttribute)) == null))
{
var targetMember = targetType.GetProperty(sourceMember.Name);
// if the property doesn't exist in the target, let's get out
if (targetMember == null) continue;
// check if this property is a Collection
bool isCollection = (sourceMember.PropertyType.IsGenericType && sourceMember.PropertyType.GetInterfaces().Any(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(IEnumerable<>)));
// if we are not dealing with associations, let's get outta herre
if (!(isCollection
? sourceMember.PropertyType.GetGenericArguments().Single()
: sourceMember.PropertyType)
.IsSubclassOf(typeof(Association))) continue;
if (isCollection)
{
IEnumerable<Association> targetCollection = (IEnumerable<Association>) targetMember.GetValue(target);
// Loop through the collection and evaluate
foreach (Association sourceAssociation in (IEnumerable) sourceMember.GetValue(source))
{
Association targetAssociation =
targetCollection.SingleOrDefault(t => t.AssociationId == sourceAssociation.AssociationId);
CopyAssociation(sourceAssociation, targetAssociation);
}
}
else
{
Association sourceAssociation = (Association) sourceMember.GetValue(source);
Association targetAssociation = (Association) targetMember.GetValue(target);
CopyAssociation(sourceAssociation, targetAssociation);
}
}
}
The problem is: It does not seem to be traversing the child objects properly and does not update the target as I would have expected. Looking for input and suggections as I am not as proficient in Reflection as I would like.
UPDATE: 12/06/2012
Ok, So I believe I've found the problem but still looking for a solution. This problem came to me from another dev on my team with the assumption that the object model was sound. However, it seems that the entity that we are currently testing does not retrieve the seeded values from the db.
There is nothing defined in the OnModelCreating() method defining this association although the tables are being properly created from the entity model and the data is being loaded properly from the dbSeed() method. I can run a select statement from the db and get the proper data. It seems that the entity model foreign key associations might be invalid and thus preventing the correct data retrieval. Any help would be greatly appreciated.
UPDATE: Ok, finally retrieving the correct data. This finally did it.
modelBuilder.Entity<ClassSubjectivityAssoc>()
.HasRequired(c => c.ClassCode)
.WithMany(u => u.Subjectivities)
.HasForeignKey(f => f.ClassCodeId);
I'm not really sure if I understand the question, but it seems that you just need to make sure that sourceMember is either an Association (or descendant) or a collection of Association.
You could do that in a quick and dirty way with casting the resulting value to Association, and checking if the cast succeeded, like this for simple properties:
Association sourceAssociation = sourceMember.GetValue(source) as Association;
if (sourceAssociation != null) // the source is a valid Association
{
Association targetAssociation = targetMember.GetValue(target) as Association;
if (targetAssociation != null) // the destionation too
{
CopyAssociation(sourceAssociation, targetAssociation);
}
}
and with similar code for the collections as well
The method you have shown will not descend a object tree, so it will only work on Association objects that are directly attached to the Entity.
See original post "UPDATES" for solution.

query base object based on a non related entity list using entity framework

I have the following model:
A workflowconfiguration has a Brand and also has a type.
A Workflowconfiguration has a collection of Approvers.
The Approvers entity has property Status and username.
In another part of the application, we have an entity called RequestBase
This entity has a string property called CurrentStatus.
I need to make a query with linq or EF Lambda expressions that returns me ALL requests which status matches the username on the approvers entity.
I know its a little bit complicated, or really much complicated, lol.
These are my entities(simplified)
public class RequestBase
{
public int RequestBaseId { get; set; }
public string CurrentStatus { get; set; }
}
public class WorkflowConfiguration
{
public int WorkflowConfigurationId { get; set; }
public WorkflowType WorkflowType { get; set; }
public Brand Brand { get; set; }
public virtual ICollection<Approver> Approvers { get; set; }
}
public class Approver
{
public Approver()
{
}
public Approver(string approverUserName)
{
Name = approverUserName;
}
public int Id { get; set; }
public string Name { get; set; }
public string StepName { get; set; } -----||||>>>>>> Must match status?
public int Order { get; set; }
}
and my query?
obviously it does not even compile
return _context.RequestBases.
.Where(a => a.CurrentStatus.Any(workflowconfiguration.Select(b=>b.Approvers.Select(c => c.StepName))));
_context.RequestBases
.Where(rb => _context.Workflowconfiguration
.Any(wc => wc.Approvers
.Any(a => a.StepName == rb.CurrentStatus)));

Categories

Resources