I have two tables in my database that are linked with 2x 1 to many relations to the same object.
Since we added the second DBLot2 to the database the list in DBLot is not filled with objects anymore.
Is there something we did wrong here?
public class DBNesting
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long DBNestingID { get; set; }
public DBLot DBLot { get; set; }
[ForeignKey("DBLot")]
public long DBLotID { get; set; }
public DBLot DBLot2 { get; set; }
[ForeignKey("DBLot2")]
public long? DBLot2ID { get; set; }
}
public class DBLot
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long DBLotID { get; set; }
public List<DBNesting> Nestingen { get; set; }
}
This is how we get the objects:
DatabaseContext dc = new DatabaseContext();
dc.DBNesting
.include("DBLot")
.include("DBLot2")
.where(...)
.ToList();
However the other side is not working:
dc.DBLot
.include("Nestingen")
.where(...)
.ToList()
I would expect that all the DBNesting where we used a DBLot in property
DBLot ore DBLot2 shoud be in Nestingen. But the collections are empty.
dc.DBLot
.include("Nestingen")
.where(...)
.ToList()
will not include the DBLot on the Nestingen only the direct object.
So it will have DBLot and a list of Nestingen but that list will not have the DBLot of each of the Nestingen in the list.
So basically you should be able to see... that you have recursion here, object has reference to object which reference itself.
dc.DBLot.include("Nestingen")
.include("Nestingen.DBLot")
.include("Nestingen.DBLot2")
.where(...)
.ToList()
may work, again will only now bring one level deeper, but if that all you need then awesome.
you could enable lazy loading... but also come with "responsibility" wouldn't recommend
ef 6 is not very efficient with include. also there is an extension which allows you to use typed version so include(x=>x.Nestingen), just take the string names out.
is the goal to have nested object relations.. nth levels. something like
Tree data structure in C#
Related
i have this class which contains a list of the object ConversieDetail
public class ConversieRun
{
[Key]
public String Guid { get; set; }
public String Naam { get; set; }
public String Status { get; set; }
public DateTime Start { get; set; }
public DateTime? Einde { get; set; }
public List<ConversieDetails> Details { get; set; }
}
With the following method i need to return a list of ConversieRun including the ConversieDetails
public List<PGData.ConversieRun> GetAll()
{
//var result = _context.CoversieDetails.ToList();
return _context.ConversieRun.ToList();
}
however when i return with above example the conversieDetail List is null.
now when i uncomment the result list, the List of conversieDetails will be filled in the ConversieRun object as expected.
any reason why the list of ConversieDetails is null if i don't get them first in another list?
thanks in advance.
Relationships in entities are not loaded by default and will be null. You can explicitly tell EF to also load the related entities by using the Include like this:
_context.ConversieRun.Include(x => x.Details).ToList();
Now all ConversieRun entities will be loaded including their details.
You can read more about this in the "Loading Related Data" section of the documentation (https://learn.microsoft.com/en-us/ef/core/querying/related-data)
The example here is using the Explicit loading method, you can also choose to use the Lazy loading method where the related entities are loaded when you request them. This can however have a negative impact on the number of database queries as it would run a separate query for every ConversieRun entity to get its details.
Can you AutoMap on objects with children of themselves?
In this example:
public class Book
{
public int? BookKey { get; set; }
public Categories bookCategories { get; set; }
}
public class Categories
{
public int? CategoryKey { get; set; }
public List<Book> RecommendedBooks { get; set; }
}
Mapper.CreateMap<Common.BookList, Book>().IgnoreAllNonExisting();
Mapper.AssertConfigurationIsValid();
Mapper.CreateMap<Common.Categories, Categories>().IgnoreAllNonExisting();
Mapper.AssertConfigurationIsValid();
Swapping the last two maps around causes errors each time. Book first, means it doesn't understand categories, and categories first means it doesn't understand books.
AutoMapper.AutoMapperConfigurationException: The following property on Common.BookList / Common.Categories cannot be mapped.
You only need to call the configuration validation once. It might make sense to have it multiple times to make debugging easier (which gives you an exception closer to the location of the mapping code), but in this case, the maps are dependent on each other (Building a map of Book requires Automapper to know how to map bookCategories).
Change the code to the below, and it will work fine
Mapper.CreateMap<Common.BookList, Book>().IgnoreAllNonExisting();
Mapper.CreateMap<Common.Categories, Categories>().IgnoreAllNonExisting();
Mapper.AssertConfigurationIsValid();
I have several tables that I need to pull data from but I do not need all of the data in all of the tables. So for example I have the following Order object that contains several child objects and object collections.
public class Order
{
public virtual int ID { get; set; }
public virtual Coupon CouponID { get; set; }
public virtual Status StatusID { get; set; }
public virtual Address ShippingAddressID { get; set; }
public virtual Address BillingAddressID { get; set; }
public virtual ICollection<OrderShipmentHistory> OrdertHistories { get; set; }
public virtual ICollection<OrderShipmentNote> OrderNotes { get; set; }
public virtual ShippingDetails ShippingDetail { get; set; }
public virtual ICollection<OrderProduct> OrderProducts { get; set; }
}
Also some of these child objects in turn have child objects and at the most extreme we have a 4 tier object hierarchy.
So my problem is that I need to retrieve a list of objects that contain only specific information from most of these child objects.
Currently when I retrieve the list of orders I am pulling everything back. I have used lazy loading so that I don't do this but I will eventually need to pull this information back as I am accessing at least one piece of data in each of these child objects.
So I was thinking that instead of populating a list of orders I could create DTO's for each of the data collections that I need. My problem is I am not sure where to start. I have seen examples of people using DTO's but are only populating them once they have retrieved all of the data. I don't want to do this. I want to only retrieve the data I need and then populate the DTO's with the result sets.
I would really appreciate any guidance on where I should start and what I should be using.
regards
Noel.
What you are talking about is called projection.
To project your object graph to a flattened structure use for example linq select.
Now within the select you can either directly create the data strongly typed with your Dto or just return an IEnumerable<T> where T is dynamic or some other Poco and pass this around...
Simple example of projection: Lets say foo is a Queryable coming from nhibernate...
// Creates anonymous type with one property 'bar'
var list = foo.Select(p => new { p.bar }).ToList();
// Creates a Dto for each element and set property Bar of the Dto.
var list = foo.Select(p => new Dto{ Bar = p.bar }).ToList();
I'm having a bit of performance problem with an EF query.
We basically have this:
public class Article
{
public int ID { get; set; }
public virtual List<Visit> Visits { get; set; }
}
public class Visit
{
public int? ArticleID { get; set; }
public DateTime Date { get; set; }
}
Now, I would like to do:
Article a = ...;
vm.Count = a.Visits.Count;
The problem is that, from what I can gather, this first causes the entire list being fetched, and then the count of it. When doing this in a loop this creates a performance problem.
I assumed that it was due to the object being "too concrete", so I've tried to move the Visits.Count call as far back in repository as I can (so that we're sort of working directly with the DbContext). That didn't help.
Any suggestions?
Assuming your data context has a Visits property:
public class MyDbContext: DbContext
{
public IDbSet<Article> Articles { get; set; }
public IDbSet<Visit> Visits { get; set; }
}
you could do that:
using (var ctx = new MyDbContext())
{
var count = ctx.Visits.Where(x => x.ArticleID == 123).Count();
}
Also if the Visits collection is not always required when dealing with an article you could declare it as IEnumerable<T>:
public class Article
{
public int ID { get; set; }
public virtual IEnumerable<Visit> Visits { get; set; }
}
and then rely on the lazy loading.
I think the performance issue might be in the lazy loading. (But need to see more code for that).
Try an include(a => a.Visits) on the moment you retrieve articles from the dbcontext.
for more inforamtion on EF performance: http://www.asp.net/web-forms/tutorials/continuing-with-ef/maximizing-performance-with-the-entity-framework-in-an-asp-net-web-application
In the end I did it another way.
I found that this was hit over and over in different ways, and due to the way the rest of the domain model is set up, I made a bit of a hack:
In my VisitRepository I created a new function GetArticleIDsWithVisit(), which makes a direct sql call via db.SqlQuery, returning a Dictionary. The dictionary is cached and used in all places where visit counts are needed.
Not very pretty, but I have wrapped it inside the repository so I think it's ok.
I'm having trouble trying to get ValueInjector to map my objects correctly. This is the code I am using for the mapping:
public IEnumerable<CategoryDTO> FindCategories(IList<object[]> criteria)
{
IEnumerable<Category> categories = _categoryRepo.Find(criteria);
IEnumerable<CategoryDTO> categoriesDto = Mapper.Map<IEnumerable<Category>, IEnumerable<CategoryDTO>>(categories);
return categoriesDto;
}
the variable categories contains a property:
IEnumerable<Standard> Standards
This property contains two Standard objects in the instance I'm calling on. The problem is when I map from my Category to my CategoryDTO. CategoryDTO is defined as this:
public class CategoryDTO : AuditableDTO
{
public Guid CategoryId { get; set; }
public string Name { get; set; }
public string MachineName { get; set; }
public string Description { get; set; }
public IEnumerable<StandardDTO> Standards { get; set; }
}
After the mapping statement is run, and I investigate the contents of categoriesDto.Standards, I can see that it is null. I would have expected my Standards to have mapped, but I'm sure I'm missing something with ValueInjector. Probably something along the lines of telling it how to map Standard to StandardDTO. Any thoughts?
EDIT: I need to clarify, I'm using this http://valueinjecter.codeplex.com/wikipage?title=Automapper%20Simulation&referringTitle=Home
EDIT 2: Digging deeper, I can see that my Iesi.Collections.HashedSet is causing the issue. Categorys' Standards property are typed as Iesi.Collections.ISet, this is turned into the HashedSet. So I guess my real question is how do I check the property for that type and how can I map?
My guess would be that the Mapper.Map doesn't know to map one level deeper than the IEnumerable. Have you tried looping though the collection, mapping it at the Category, CategoryDTO level vs the IEnumerable level?