entity framework core reuse a set of include statements - ef-core-2.0

How can I clean up my entity 'includes' to reuse the same set of statements? I'm trying to reuse a set of Includes while staying DRY.
Before
_context.Accounts
.Include(x=> x.Status)
.Include(x=> x.Type)
.Include(x=> x.Phones)
.Include(x=> x.Users)
.Include(x=> x.Admins)
After:
_context.Accounts.CustomIncludes()

Try this:
public static class DataExtensions
{
public static Microsoft.EntityFrameworkCore.Query.IIncludableQueryable<Account, List<Admin>> CustomIncludes(this DbSet<Account> accounts)
{
return accounts
.Include(p => p.Status)
.Include(p => p.Type)
.Include(p => p.Phones)
.Include(p => p.Users)
.Include(p => p.Admins);
}
}
Then you can say
context.Accounts.CustomIncludes();

Related

How do you properly delete Dependent entites when updating the Principal entity?

I'm having an issue with EFCore 2.0 where its impossible to properly delete dependent entities when updating the prinicipal entity. Here's my code:
Mapping
public class BlueprintMap : IEntityTypeConfiguration<Blueprint>
{
public void Configure(EntityTypeBuilder<Blueprint> builder)
{
builder.ToTable("blueprint").HasKey(t => t.Id);
builder.Property(x => x.Id).HasColumnName("blueprint_id").ValueGeneratedOnAdd();
builder.HasMany<Objective>().WithOne(x => x.Blueprint).OnDelete(DeleteBehavior.Cascade);
}
}
public class ObjectiveMap : IEntityTypeConfiguration<Objective>
{
public void Configure(EntityTypeBuilder<Objective> builder)
{
builder.ToTable("objective").HasKey(x => x.Id);
builder.Property(x => x.Id).HasColumnName("objective_id").ValueGeneratedOnAdd();
builder.HasOne(x => x.Blueprint).WithMany(y => y.Objectives).HasForeignKey("blueprint_id");
}
}
Update WebApi Call
Get from context
var blueprint = await blueprintContext.Blueprints
.Where(x => id == x.Id)
.Include(x => x.Objectives)
.SingleOrDefaultAsync();
Remove objectives from blueprint entity (Objectives is ICollection, the dependent entity
blueprint.Objectives.Remove(objective);
// Get a new context
var blueprintContext = await _blueprintContextFactory.CreateContext();
Blueprint entity AND Blueprint from context have 0 objectives
int x = await blueprintContext.SaveChangesAsync();
Get WebApi Call
Get from context
return await blueprintContext.Blueprints
.Where(x => id == x.Id)
.Include(x => x.Objectives)
.SingleOrDefaultAsync();
blueprint has 2 objectives again.
Stupid mistake. The first context tracks the objectives being removed. When a new context is generated to save the changes, it doesn't know about the objectives being removed so they remain and regenerate. In other words, the Update request is using multiple contexts instead of just 1 per request and this is unacceptable by EF Core's design.

OneToOne mapping with NHibernate and Breeze

Is it possible to make that kind of mapping work with breeze and NHibernate:
public class PeopleMap: BaseMapping<People>
{
public PeopleMap()
{
this.Property(x => x.FirstName);
this.Property(x => x.LastName);
}
}
public class PersonMap : JoinedSubclassMapping<Person>
{
public PersonMap()
{
this.Key(p=>p.Column("ID"));
this.Property(x => x.FirstName);
this.Property(x => x.LastName);
this.Property(x => x.InfoId, map =>
{
map.Insert(false);
map.Update(false);
}
);
this.ManyToOne(x => x.Info, map =>
{
map.Cascade(Cascade.All);
map.Column("InfoId");
});
}
public class PersonInfoMap : BaseMapping<PersonInfo>
{
public PersonInfoMap()
{
this.Property(x => x.Extra);
this.OneToOne(x => x.Person, map =>
{
map.Constrained(true);
map.PropertyReference(p => p.Info);
});
}
}
There is a table per subclass inheritance between people and person. The goal is to make a one to one association between person and personinfo. The mapping works fine in NHibernate. The metadata are generated and queries can be done. The only problem is I can't do a save.
var d = breezeService.manager.createEntity('Person',
{
FirstName: 'Laurent',
LastName: 'Nullens'
});
var l = breezeService.manager.createEntity('PersonInfo',
{
Extra: 'First data',
Person: d
});
d.Info = l;
The result is an exception because the Person entity is saved before the PersonInfo(foreign key exception). I saw in the samples a one to one with Order and InternationlOrder but in that sample both entities share the same primary key.
Is it possbile or is there any workaround like in the Order/InternationalOrder sample?
Have you looked at the breezjs NHibernate sample?
Please do ... and then report back if you can't find what you're looking for.

Selecting nested navigation properties will generate the same SQL statement when selecting separate navigation properties

I have the following repository method:-
public AccountDefinition GetCustomer2(int id)
{ var c = entities.AccountDefinitions.Where(p => p.ORG_ID == id)
.Include(a => a.SDOrganization)
.Include(a2 => a2.SiteDefinitions)
.Include(a3 => a3.SDOrganization.AaaPostalAddresses)
.Include(a4 => a4.SiteDefinitions.Select(a5 => a5.DepartmentDefinitions.Select(a6 => a6.SDUsers.Select(a7 => a7.AaaUser.AaaContactInfoes)))).SingleOrDefault();
return c; }
But when I comment some code I found that the generated SQL statement when calling the Action method will be the same:-
public AccountDefinition GetCustomer2(int id)
{ var c = entities.AccountDefinitions.Where(p => p.ORG_ID == id)
// .Include(a => a.SDOrganization)
// .Include(a2 => a2.SiteDefinitions)
.Include(a3 => a3.SDOrganization.AaaPostalAddresses)
.Include(a4 => a4.SiteDefinitions.Select(a5 => a5.DepartmentDefinitions.Select(a6 => a6.SDUsers.Select(a7 => a7.AaaUser.AaaContactInfoes)))).SingleOrDefault();
return c; }
So does this mean when I navigate to a nested Navigation property , then EF will automatically retrieve parent navigation properties also? . so for example when i write .Include(a3 => a3.SDOrganization.AaaPostalAddresses) , then there is no need to write .Include(a => a.SDOrganization)
In a nutshell yes, it has to otherwise there is no way for you to traverse to that navigation property
EG, the only way to access AccountDefinition.SDOrganization.AaaPostalAddresses is if SDOrganization is not null.
Having said that my personal preference is to make this intermediate inclusion explicit by listing it anyway. While this has no functional benefit its a reminder that this property will also be returned

MVC 4 explicitly load a many to many EF lookup and filter by both keys

I have the following in an MVC4 app. I want to filter the load by both keys, there could be many to many but both the keys together represent a unique row. The goal to to explicitly load these collections only when needed and filter by both sides of the relationship.
I have the following Entity.DBContext, it works but only for the UserId key.
context.UserProfiles.Include(o => o.CoachOfOrginizations).Where(p => p.UserId == UserId).Single()
Also this loads all of them, but of course doesnt filter at all, just showing you so you know that I set up my fluent code properly.
context.Entry(this).Collection(t => t.AdministratorOfOrginizations).Load();
modelBuilder.Entity<UserProfile>()
.HasMany(p => p.CoachOfOrginizations)
.WithMany(t => t.Coaches)
.Map(mc =>
{
mc.ToTable("OrganizationCoaches");
mc.MapLeftKey("UserProfileID");
mc.MapRightKey("OrganizationID");
});
CoachOfOrginizations is the following property on UserProfile.
public ICollection<Organization> CoachOfOrginizations { get; set; }
What i would like is to filter my original .Include above based on Organization.ID (the other key) and the currently used key p.UserId. I've tried the following and it doesn't work, any advice? "this" is the UserProfile object.
context.Entry(this)
.Collection(b => b.CoachOfOrginizations)
.Query()
.Where(p => p.ID == orgID)
.Load();
You can do both filtering in a single query. See conditional includes.
var query = context.UserProfiles
.Where(p => p.UserId == UserId)
.Select(p => new { p, Orgs = p.CoachOfOrginizations.Where(o => o.ID == orgID) });
var profiles = query.AsEnumerable().Select(a => a.p);

Nested resolution

Say I have:
ProductA
ProductB
ProductScreen
ProductAScreen1 : ProductScreen
ProductAScreen2 : ProductScreen
ProductBScreen1 : ProductScreen
ProductBScreen2 : ProductScreen
How can I set it up so that I register the screens locally to the product? So when I am in ProductA and passing IEnumerable it doesn't resolve ProductB's screens?
I thought this would be achievable using something like the lifetime scope, but it doesn't appear I understood it correctly.
Lifetime scope is used to control instance lifetime. You are talking about controlling selection. For that, you should look at the metadata features in Autofac.
Using metadata, you can "tag" each product screen, indicating which product it belongs to:
builder.Register(c => new ProductAScreen1()).As<ProductScreen>()
.WithMetadata<IProductScreenMetadata>(m =>
m.For(am => am.ProductType, typeof(ProductA)));
builder.Register(c => new ProductAScreen2()).As<ProductScreen>()
.WithMetadata<IProductScreenMetadata>(m =>
m.For(am => am.ProductType, typeof(ProductA)));
builder.Register(c => new ProductBScreen1()).As<ProductScreen>()
.WithMetadata<IProductScreenMetadata>(m =>
m.For(am => am.ProductType, typeof(ProductB)));
builder.Register(c => new ProductBScreen2()).As<ProductScreen>()
.WithMetadata<IProductScreenMetadata>(m =>
m.For(am => am.ProductType, typeof(ProductB)));
Next, you can take a dependency on a IEnumerable<Lazy<ProductScreen, IProductScreenMetadata>> and resolve screens according to product type:
var productScreens = _screens.WHere(a => a.Metadata.ProductType == typeof(ProductA));
Update: for completeness, here is a simpler solution using the Keyed approach. First, registration is much simpler:
builder.RegisterType<ProductAScreen1>().Keyed<ProductScreen>(typeof(ProductA));
builder.RegisterType<ProductAScreen2>().Keyed<ProductScreen>(typeof(ProductA));
builder.RegisterType<ProductBScreen1>().Keyed<ProductScreen>(typeof(ProductB));
builder.RegisterType<ProductBScreen2>().Keyed<ProductScreen>(typeof(ProductB));
To resolve a collection of keyed services we'll have to take a dependency on the IIndex<,> type:
public class SomeService
{
private IEnumerable<ProductScreen>> _screens;
public SomeService(IIndex<Type, IEnumerable<ProductScreen>> screens)
{
_screens = screens;
}
public void DoSomething()
{
var screensForProductA = _screens[typeof(ProductA)];
}
}
Note: for the curious: instead of hardcoding the type registrations, here's how you can do a "by convention" registration:
var assembly = ...;
var productTypes = ...; // a collection of all the product types
foreach(var productType in productTypes)
{
builder.RegisterAssemblyTypes(assembly)
.Where(t => typeof(ProductScreen).IsAssignableFrom(t))
.Where(t => t.Name.StartsWith(productType.Name))
.Keyed<ProductScreen>(productType);
}

Resources