EF Code first Eager loading problem - ef-code-first

I have two entities in 1:n relationship: Category and Product.
public class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public virtual Product { get; set; }
}
public class context : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
I want to load products in every category by Eager loading.
context.Categories.Include(c=>c.Products)
but Include do not load any navigation property. it accept only one parameter called "path" typed string.

Are you missing a using? VS 2010 is a bit dumb on this one and you often have to explicitly put in:
using System.Data.Entity
..to get the lambda include option available.
It won't prompt you to add it as it already has a string based definition for Include available under
System.Linq
.Include(x => x.MyObject) is actually a new extension method for the existing linq method.

Related

EF Core one-to-many relationship with multiple contexts (databases)

I have contexts with entities like this:
public class CompanyContext : DbContext
{
public DbSet<StoreModel> Stores { get; set; }
// Other entities
}
public class DepartmentContext : DbContext
{
public DbSet<OrderModel> Orders { get; set; }
// Other entities
}
public class StoreModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<OrderModel> ReceivedOrders { get; set; }
public virtual ICollection<OrderModel> PreparedOrders { get; set; }
public virtual ICollection<OrderModel> IssuedOrders { get; set; }
}
public class OrderModel
{
public Guid Id { get; set; }
public string Details { get; set; }
public StoreModel GettingStore { get; set; }
public StoreModel PreparingStore { get; set; }
public StoreModel IssuanceStore { get; set; }
}
For example a user makes an order in storeA, but wants to receive it in storeC, and it order will preparing in storeB. And I needs a statiscics about store received/prepared/issued orders.
When I try to create a migrations, EF throws exceptions "Unable to determine the relationship represented by navigation 'OrderModel.GettingStore' of type 'StoreModel'" and "Unable to determine the relationship represented by navigation 'StoreModel.IssuedOrders' of type 'ICollection<OrderModel>'". If I understand correctly, this happens because entities are defined in different contexts.
Now I just use next model:
public class OrderModel
{
public Guid Id { get; set; }
public string Details { get; set; }
public Guid GettingStoreId { get; set; }
public Guid PreparingStoreId { get; set; }
public Guid IssuanceStoreId { get; set; }
}
This works fine, but perhaps there are options that allow to create such a structure using navigation properties, with correct relationships between these entities from different contexts(databases).
First, the map of a different database was not placed in tables of different application formats, so think that you have a domain that should be well defined in your application, that way you would have the mapping of your application like this:
public class DomainNameContext: DbContext
{
public DomainNameContext(): base()
{
}
public DbSet<StoreModel> Stores { get; set; }
public DbSet<OrderModel> Orders { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// config mapping methods
}
}
another thing, the relation you are using doesn't work so you can't have a repetition of Orders within the same class because this is not one -> many, this statement means that a StoreModel line can have many lines in the OrderModel this way would be like this
public class OrderModel
{
public Guid Id { get; set; }
public string Details { get; set; }
public Guid StoreModeId { get; set; } // this part will show the entity framework that this is the fk it will correlate
public StoreModel StoreModel { get; set; }
}
public class StoreModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<OrderModel> OrderModels { get; set; }
}
see that if you are wanting to have many StoreModel related to many OrderModel then you need to use many -> many which microsoft documentation foresees to use as well
good to map this within its context it is necessary in OnModelCreating to use its mapping like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// config mapping methods
modelBuilder.Entity<StoreModel>()
.HasMany<OrderModel>(g => g.OrderModels )
.HasForeignkey<Guid>(s => s.StoreModeId )
}
you can have a look at the microsoft documentation enter link description here, enter link description here
now if you need to map between contexts you will have to use dapper to make separate queries in separate bases the entity has support for that in this link enter link description here
and then you can make the necessary inner joins so that you can use it but natively this does not exist, I advise you to rethink your database so that it can make more sense to a relational model, perhaps putting types for your StoreModel and OrderModel so you can use the way I wanted the types GettingStore, PreparingStore, IssuanceStore using an enum for this to make it explicit

ASP.NET MVC Entity Frame Work : Code First Approach

I am working ASP.NET MVC using Entity frame work code first approach. I am facing an issue ,the issue is as i have dropped two tables by using Package Manager Console.
Now i am trying to add the tables, the tables are not getting added using Package Manager Console.
Getting Error Cannot find the object "dbo.Movies" because it does not exist or you do not have permissions.
Even in Indentity.cs Model [Movies] is mentioned.
public DbSet Movies { get; set; }
Below are the Model.
public class Movie
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime ReleaseDate { get; set; }
public Genre Genres { get; set; }
public byte GenreId { get; set; }
public DateTime DateAdded { get; set; }
public int NumberOfStocks { get; set; }
}
public class Genre
{
public byte Id { get; set; }
public string Type { get; set; }
}
How to the add the new table in existing Entity frame work using Code first approach.

Can't add another table (migration) into project ASP.NET

My project is supposed to have 2 tables: articles and categories with one-to-many relation (article can have only one category but category can be owned by many articles) between each other. I tried to add table Articles (with category as just a string field) first and it passed successfully (table has been created). Then I add Category class and add some fields into Article class. Now I have smth like this:
public class Article
{
public int Id {get; set; }
public string Name { get; set; }
public string ShortDescription { get; set; }
public string Description { get; set; }
public byte[] HeroImage { get; set; }
public DateTime Date { get; set; }
public int? CategoryId { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Article> Articles { get; set; }
public Category()
{
Articles = new List<Article>();
}
}
After all thee changes I changed Context:
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Article> Articles { get; set; }
public DbSet<Category> Categories { get; set; }
}
And then I try to add migration (Add-Migration Categories -> Update-Database), passed without any mistakes but nothing changes! Table Categories hasn't been created and even table Articles hasn't been changed. Please, any help.
Remove last migration
dotnet ef migrations remove --force
Create migration again
dotnet ef migrations add <Name>
Check that method "Up" in your migration has code for creating table and changing fields names of Article table.
Update database
dotnet ef database update

EF6 automatically includes nested objects

Probably a simple question, but there is something I can't get my head around.
My structure Bundle -> BundleMembers -> InsuranceTypes
When I retrieve a single record form BundleMembers, and I include Bundle. EF6 automatically includes all BundleMembers in the Bundle
Example:
public async Task<List<BundleMember>> GetBundleMembers(string userId, bool includeBundle, bool includeInsuranceTypes)
{
var bundleMembers = db.BundleMembers.Where(m => string.Equals(m.UserId, userId, StringComparison.CurrentCultureIgnoreCase));
if (includeBundle)
{
bundleMembers = bundleMembers.Include(o => o.Bundle);
}
if (includeInsuranceTypes)
{
bundleMembers = bundleMembers.Include(m => m.BundleMemberInsuranceType);
}
return await bundleMembers.ToListAsync();
}
I call the function like this:
GetBundleMembers(_userManager.GetUserId(User), true, false)
Do I have to access the data from Bundle, to avoid this?
EDIT 1:
My data model looks like this:
public class Bundle
{
public int BundleId { get; set; }
public State State { get; set; }
public ICollection<BundleMember> Members { get; set; }
public ICollection<InviteLink> InviteLinks { get; set; }
public string BundleName { get; set; }
public string Description { get; set; }
public string ImagePath { get; set; }
}
public enum State
{
NotApproved,
Approved,
Disabled,
Rejected
}
public class BundleMember
{
public ApplicationUser User { get; set; }
public string UserId { get; set; }
public int BundleMemberId { get; set; }
public int BundleId { get; set; }
public Bundle Bundle { get; set; }
public bool Admin { get; set; }
public int Price { get; set; }
public int Coverage { get; set; }
public ICollection<BundleMemberInsuranceType> BundleMemberInsuranceType { get; set; }
}
I did not include BundleMemberInsuranceType and InviteLink as they are working fine.
Relevant part of ApplicationDbContext:
public DbSet<Bundle> Bundles { get; set; }
public DbSet<BundleMember> BundleMembers { get; set; }
As suggested in comments:
The described behavior is actually expected. Since includeBundle is set to true, both Bundles and referenced BundleMembers are in the context, and relationship fixup will set all navigation properties according to the FK relationships.
Obviously, this works both from BundleMembers to Bundles and from Bundles to BundleMembers since .Include does nothing more than create the SQL statements to load the related entries into the context as well and relationship fixup will do the rest.
To have the Bundles not have BundleMembers, you'll have to load them without the BundleMembers in the context and set the navigation properties yourself (EF will always set both direct and inverse navigation properties). In order to do this, there are two main ways:
Either load your bundles in a fresh context without the previous loaded BundleMembers (best practice is to load them into memory since EF navigation properties are loaded due to eager loading; you could have entries attached to two contexts and an exception will be thrown) or
Detach your BundleMembers from the context before loading the Bundles into it.

Mapping domain model to logical model using automapper

My problem is with loop navigation property during mapping domain models to logical models using Automapper.
Here are examples of domain models:
public class Customer
{
public long CustomerId { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Payment> Payments { get; set; }
}
public class Payment
{
public long PaymentId { get; set; }
public virtual Customer Customer { get; set; }
}
And here logical models:
public class CustomerModel
{
public long CustomerId { get; set; }
public string Name { get; set; }
public IEnumerable<PaymentModel> Payments { get; set; }
}
public class PaymentModel
{
public long PaymentId { get; set; }
public CustomerModel Customer { get; set; }
}
And now. When i perform mapping using automapper like this:
public IQueryable<CustomerModel> GetCustomers()
{
return _db.Customer.ProjectTo<CustomerModel>();
}
}
I'm getting System.StackOverflowException. As i understand it's because of loop in navigations. After mapping Customer to CustomerModel Automapper is trying to map ICollection Payments to IEnumerable Payments but because of navigation to CustomerModel inside PaymentModel class its looping..
I know i could fix it by creating 2nd logical model (class Payment2)without navigation property to CustomerModel but i would have to perform another mapping. And with 50 domain models with navigation property it would be annoying.
Is there any other mayby easier and faster solution?
I suggest to change type of Payments property:
public class CustomerModel
{
public List<PaymentModel> Payments { get; set; }
}
And manually map it:
Mapper.CreateMap<Payment, PaymentModel>()
.ForMember(dest => dest.Customer, opt => opt.Ignore());
Mapper.CreateMap<Customer, CustomerModel>()
.AfterMap((src, dest) => dest.Payments.ForEach(p => p.Customer = dest));
EDIT: Unfortunately it will work only in case when mapping is started from Customer entity:
CustomerModel model = Mapper.Map<CustomerModel>(entity);
If you don't care about perfomance, try DynamicMap:
CustomerModel model = Mapper.DynamicMap<CustomerModel>(entity);

Resources