Entity Framework Conditional Include() - entity-framework

public partial class Table
{
public int TableId { get; set; }
public Nullable<int> Num { get; set; }
public int NbSeats { get; set; }
public Nullable<int> R_LocationId { get; set; }
public virtual ICollection<Availibility> Availibilities { get; set; }
public virtual R_Locations R_Locations { get; set; }
}
public partial class Availibility
{
public System.DateTime DayRes { get; set; }
public Nullable<bool> Available { get; set; }
public int TableId { get; set; }
public virtual Table Table { get; set; }
}
I would like to implement this request with entity Framework:
select *
from Tables join Availibilities
on Availibilities.TableId = Tables.TableId
where Availibilities.Available=1

One of the slower parts of fetching data from a database is the transport of the data from the DBMS to your local process. Hence you should not transport more values than you actually plan to use.
Your Table has zero or more Availabilities. Your database implements this by giving the Availability table a foreign key to the Table that it belongs to.
So if you have a table with Id 4, which has 100 Availabilities, and you would query the Table with its Availabilities using a Join and Include you would transfer the foreign key Availability.TableId a 100 times, while you already know they will all have the value of Table.Id. You even know this value, because you asked for table with Id 4.
Hence, unless you plan to Update retrieved values, always use Select instead of querying complete classes.
Back to your question
Given a tableId, you want information of the table together with (some or all) its Availabilities.
Thanks to entity framework you don't have to use Join. If you use the collections, entity framework will do the join for you.
var tableWithAvailabilities = myDbContext.Tables
.Where(table => table.TableId == tableId)
.Select(table => new
{
// select only the properties you plan to use
Id = table.TableId,
Num = table.Num,
...
Availabilities = table.Availabilities
.Where(availability => ...) // if you don't want all Availabilities
.Select(availability => new
{
// again: select only the properties you plan to use
// not needed, you know it equals Table.TableId
// Id = availability.TableId,
Date = availability.DayRes,
Available = availability.Avialable,
})
.ToList(),
});
Entity framework knows which Join is needed for this. One of the nicer things when using the Collections instead of a Join is that you make your database more abstract: you really are thinking of a Table that has zero or more Availabilities. It is a shortage of a DBMS that it needs two tables to implement this. If your internal tables changes, for instance the name of the foreign key, your query does not change, because you don't use the foreign key
If you are not planning to update a fetched value, then it is seldom wise to fetch

There is two way to filter include Entity.
Using a projection (See #Harald answer)
Using a third party library
Disclaimer: I'm the owner of the project Entity Framework Plus
(
The EF+ Query IncludeFilter allow easily filter included entities. It works with EF6.x
return context.Tables
.IncludeFilter(x => x.Availibilities .Where(c => c.Available == 1))
.ToList();
Under the hood, the library does exactly a projection.
One limitation is all Include but now be called with IncludeFilter even if no filter is specified such as the account.
Wiki: EF+ Query Include Filter

Related

Configuring relationships between non-PK columns in database-first EF Core

I'm trying to configure relationships between entities in EF Core 6, using an existing database as a starting point. I'm running into problems when the joins between tables are not between primary key columns. I scaffolded the C# classes automatically, so there may be problems caused by naming column renaming and mapping that I'm not sure exactly where I'm getting lost.
For example, I'm defining the relationships between the Employees and the Trips they've taken. The relevant tables/columns in SQL Server look like the following:
trips
trip_id (PK)
trip_employee_id
...
employees
employee_key (PK)
valid_date
invalid_date
employee_id
...
One thing to note is that employee_id is actually not a unique identifier within employees, but the combination of employee_id + invalid_date = NULL is. So the following query would be a join between them, assuming we want the most up-to-date info on the associated employee:
SELECT *
FROM dbo.trips t
INNER JOIN dbo.employees e
ON t.trip_employee_id = e.employee_id
AND e.invalid_date = NULL
I'm using DataAnnotations as much as I can, so my C# classes look like the following:
namespace Models
{
[Table("trips")]
public partial class Trip
{
[Column("trip_id")]
[Key]
public int TripId { get; set; }
[Column("trip_employee_id")]
public string TripEmployeeId { get; set; }
// Other columns below
}
[Table("employees")]
public partial class Employee
{
[Column("employee_key")]
[Key]
public int EmployeeKey { get; set; }
[Column("valid_date", TypeName = "datetime")]
public DateTime ValidDate { get; set; }
[Column("invalid_date", TypeName = "datetime")]
public DateTime? InvalidDate { get; set; }
[Column("employee_id")]
public string EmployeeId { get; set; }
// Other columns below
}
}
Is there a way to do this within EF Core, with or without DataAnnotations? And automatic filtering on invalid_date also isn't necessary, just a nice to have, since I assume I could configure a 1-Many relationship from trips to employees and then just filter on employees where invalid_date = NULL.
The main question is just whether relationships are possible to configure when neither the principal or dependent entities link on the PK.

How to Find a record in Entity Framework without a Primary Key

I've got an entity:
public class Account
{
public int AccountId { get; set; }
public string Mnemonic { get; set; }
public decimal NetAssetValue { get; set; }
}
On this entity I have a primary key (AccountId) and an alternate unique index on the mnemonic.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Account
modelBuilder.Entity<Account>()
.HasKey(a => a.AccountId);
modelBuilder.Entity<Account>()
.HasIndex(a => a.Mnemonic)
.IsUnique();
}
When I store my test data in an XML file, I have no knowledge of the value assigned to the primary key, so I need to find this record by the Mnemonic if I want to use it.
I know I can use LINQ:
var accountId = (from a in account
where mnemonic = "Account1"
select AccountId).First();
But will this use the index or iterate over the entire collection. I could have thousands of accounts and don't want do be executing a table scan each time I want to find an account when I'm loading from my external files.
Provided account is the DbSet<Account> from your DbContext or an IQueryable<Account> derived from he same, it will query the database using the index. if you want to emulate Find (where the locally tracked entities are checked for the entity prior to querying the database) you can first check if dbContext.Set<Account>().Local contains the entity prior to querying the database.

Cannot insert explicit value for identity column - into related table

I have a database first model.
My application UI provides a group of checkboxes, one for each value in Data_Type.
When the user checks one, I expect a row to be added in BUS_APPL_DATA_TYPE,
however I'm getting an error about Cannot insert explicit value for identity column in DATA_TYPE (And I absolutely do not actually want to insert data in this table)
My EF Model class for BUS_APPL has this property
public ICollection<BusApplDataType> BusApplDataType { get; set; }
And that EF Model class looks like
public partial class BusApplDataType
{
public int BusApplId { get; set; }
public int DataTypeId { get; set; }
[Newtonsoft.Json.JsonIgnore]
public BusAppl BusAppl { get; set; }
public DataType DataType { get; set; }
}
What exactly do I need to add to the BusApplDataType collection to get a record to be inserted in BUS_APPL_DATA_TYPE?
Edit:
At a breakpoint right before SaveChanges.
The item at index 2 is an existing one and causes no issues.
The item at index 3 is new. Without this everything updates fine. There is a DATA_TYPE with id 5 in the database.
The surrounding code, if it helps.
[HttpPut("{id}")]
public IActionResult Update(int id, [FromBody] BusAppl item)
{
...
var existing = _context.BusAppl.FirstOrDefault(t => t.Id == id);
...
existing.BusApplDataType = item.BusApplDataType; //A bunch of lines like this, only this one causes any issue.
...
_context.BusAppl.Update(existing);
_context.SaveChanges();
return new NoContentResult();
}
My issue was that I needed to use my context to look up the actual entity, using info passed, instead of using the one with all the same values that was passed into my api directly.

Entity Framework Code First Foreign Key Problems

I am trying to use EF Code First on an existing database. I first tried some of the reverse-engineering tools, but I ran into problems with that, so at the moment I am trying to hand-code some of the classes. I am having some trouble getting some of the foreign key relationships set up. Consider two tables. The first is called LocaleValueLookup:
public class LocaleValueLookup
{
public int Id { get; set; }
public Guid Guid { get; set; }
}
This table provides an Id for multi-language text held in a different table (that other table is not important for the purposes of this question). The second table is called SectionType, and it has an optional FK to LocaleValueLookup:
public class SectionType
{
public int EnumId { get; set; }
public string Name { get; set; }
public int? DefaultSectionTextLocaleValueLookupId { get; set; }
// Navigation property
public LocaleValueLookup DefaultSectionTextLocaleValueLookup { get; set; }
}
I have tried various things, including adding a [ForeignKey] attribute to the SectionType.LocaleValueLookup property, and various incantations in the DbContext.OnModelCreating() override, but when I query the DbContext, I can't get the DefaultSectionTextLocaleValueLookup to be anything but null. I can retrieve other objects from the context just fine, and I have verified that DefaultSectionTextLocaleValueLookupId is not null at least some of the time.
My OnModelBuilding() contains the following:
modelBuilder.Entity<LocaleValueLookup>()
.ToTable("LocaleValueLookup")
.HasKey(lvl => lvl.Id);
modelBuilder.Entity<LocaleValueLookup>().Property(lvl => lvl.Id).IsRequired();
modelBuilder.Entity<SectionType>()
.ToTable("SectionType")
.HasKey(st => st.EnumId);
modelBuilder.Entity<SectionType>().Property(st => st.EnumId).IsRequired();
A couple of other points:
I would prefer not to have a SectionType collection on the LocaleValueLookup object. LocaleValueLookup is a low-level class that a lot of other classes depend on, so to include a collection property on LocaleValueLookup for every other class that references it will make for an unwieldy class with a lot of collections on it that I don't need from a domain perspective.
I would prefer to do the mapping setup in DbContext.OnModelCreating() rather than using attributes on my model objects
Any help would be greatly appreciated!
It looks like your foreign key is nullable so that means an optional -> many relationship.
Could you try something like this:
modelBuilder.Entity<SectionType>()
.HasOptional(opt => opt.DefaultSectionTextLocaleValueLookup)
.WithMany() // no navigation on the other side
.HasForeignKey(fk => fk.DefaultSectionTextLocaleValueLookupId);
If you were to write a query like this you should get a value back:
var query =
from st in db.SectionTypes
where st.EnumId == 12345
select new
{
SectionType = st,
LocaleValue = st.DefaultSectionTextLocaleValueLookup
};
It will only be non-null if the foreign key has a value, obviously.

Mapping properties to (differently named) foreign key fields in Entity Framework CTP5

I'm trying to use the Entity Framework CTP5 Fluent API to map an exist database. I have the following classes:
public class Shop
{
public long Id
{
get;
set;
}
}
public class Sale
{
public long Id
{
get;
set;
}
public virtual Shop Shop
{
get;
set;
}
}
The corresponding tables are called "Stores" and "Sales". Sales has a StoreId foreign key that points to the Id field in the Stores table.
I'm struggling to map the Sale.Shop.Id to the StoreId in the table. I'm not at liberty to change it to ShopId, so need to map it.
In CTP4, I was using:
modelBuilder.Entity<Sale>().MapSingleType(x =>
new
{
Id = x.Id,
StoreId = x.Shop.Id
});
I tried the following:
modelBuilder.Entity<Sale>().Property(x => x.Shop.Id).HasColumnName("StoreId");
However, it seems this only works with a primitive type.
How do I specify this mapping?
Update: I've added a revised version for the Release Candidate of EF 4.1 below
After some hunting, I've found the answer that works for me:
EF4.1 RC version:
modelBuilder.Entity<Booking>().HasRequired(b => b.Booker)
.WithMany(m => m.BookedSlots).Map(p=>{
p.MapKey("BookerID");
});
in your case:
modelBuilder.Entity<Sale>().HasRequired(sale => sale.Shop)
.WithMany().Map(s=> {
s.MapKey("StoreId");
});
My version is slightly different because I have navigation properties on both sides of the relationship.
I think the best way to solve this would be to upgrade your independent Association to be a Foreign Key Association meaning that instead of hiding the foreign key ShopId, actually including it in Sale class. Then you can use Data Aannotations/Fluent API to change its column name to match to your existing schema:
public class Shop
{
public long Id { get;set; }
}
public class Sale
{
public long Id { get; set; }
[Column(Name="StoreID")]
public long ShopId { get; set; }
public virtual Shop Shop { get; set; }
}
Which results to the desired DB Schema:
I think what you're looking for is the RelatedTo attribute. More information in this ADO.NET team blog post.