I have a model in EF similar to this
Person
PK Guid Id
PK DateTime DateSynced
Test
PK Guid Id
FK Guid PersonId
In Entity Framework 6.2, I really only care about Navigation Property on Person with reference to a collection of Tests. I do not need a property of Test.Persons or anything like that.
I really just want to have Person.Tests where Test.PersonId = Id regardless of DateSynced. There will eventually be many persons with the same Id, each with a different DateSynced DateTime.
Is this doable or do I need a Many-To-Many with an intermediate table?
I understand that EFCore has a concept of Alternate Keys and I thought I might could leverage that in this effort, but there does not seem to be a corresponding functionality in EF 6.2
Edit
I have the following Fluent rule in my OnModelCreating override.
modelBuilder.Entity<Test>()
.HasRequired(t => t.Person)
.WithMany(p => p.Tests)
.HasForeignKey(t => t.PersonId);
I get the following exception complaining about the Dependent and Principal role constraints:
The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
As I understand it, this is because I am referencing an entity with a 2-part composite key by only one part of that key. What I am looking for is a way to do exactly that.
The foreign key on the second table must match the PK on the first.
For example, using Int for the key type for simplicity.
If I have a Person with a PK of ID: 1, SyncDate: 2019-05-22
Then I add a second variant: PK of ID: 1, SyncDate: 2019-05-23
If I go to add a "Test" record, which Person record would it reference with a FK Person ID of 1? It would reference both records, hence EF cannot support a reference of "HasRequired" pointing to a single Person record.
To reference one variant of Person ID 1, your Test record will need both a PersonId and a SyncDate to identify the record:
public class Test
{
public Guid Id { get; set; }
public Guid PersonId { get; set; }
public DateTime SyncDate { get; set; }
public virtual Person Person { get; set; }
}
modelBuilder.Entity<Test>()
.HasRequired(t => t.Person)
.WithMany(p => p.Tests)
.HasForeignKey(t => new { t.PersonId, t.SyncDate });
Tables in the database cannot reference each other based on a partial FK unless a many to 1. I.e.
Person
------
PK: PersonID
PersonComment
-----------
PK: PersonId
PK: CommentDate
In this example a Person can have many PersonComments based on the PersonID link, while a Comment can resolve back to the Person via the Person ID. As a FK.
In a table structure that has:
Person
------
PK: PersonID
PK: Version
PersonComment
-------------
PK: PersonCommentID
PersonId
PersonId in the PersonComment cannot be a FK to Person because it doesn't reflect the PK to the Person table. You can legally have this table structure, but PersonId is just a dumb, unconstrained column. You can query all Person records manually using it, but you will get all Versions of the person. There are no constraints etc. to ensure that the Person ID on a comment matches an Id on the Person table.
If you don't care about the versions of a Person You can have a Test entity with a Person ID, but EF can't associate that to Person entities, you'll have to load Person records manually from the Context.
When it comes to the purpose behind your schema structure, I would suggest looking at possible alternatives. For instance, if your goal is to track versioned data I would suggest looking at something like:
Person
PK: PersonId
** Person Fields
PersonHistory
PK: PersonHistoryId
FK: PersonId
VersionDate
** Person Fields
Test
PK: TestId
FK: PersonId (if applies to current person, or PersonHistoryId if a specific version)
Then "Person" reflects the person in it's current state, containing a collection reflecting the History. From there you can prevent modifying the Person fields via private setters and DDD-style methods which would be responsible for composing a new History record based on the current Person data before updating the Person values. This way a Person record can be historical and preserve it's ID for related entities.
Related
I have a table as follows:
CREATE TABLE MyTable
(
ID INT NOT NULL PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
LARGEBLOB VARBINARY(MAX) NULL
)
With an Entity defined as:
public class Entity
{
public int Id {get;set;}
public string Name {get;set;}
public virtual byte[] LargeBlob {get;set;}
}
99% of my use cases involve displaying ID and NAME only.
1% of the time I need LARGEBLOB.
Is there any way I can mark LargeBlob as Lazily Loaded so to avoid
huge wasted data transfers? Alternatively, are there other ways of
achieving the same outcome?
I tried splitting into 2 tables with a 1->[0|1] relationship as follows:
CREATE TABLE MyTable
(
ID INT NOT NULL PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
LARGEBLOBID INT NULL
)
CREATE TABLE MySubTable
(
ID INT PRIMARY KEY,
LARGEBLOB VARBINARY(MAX) NOT NULL
)
with entities
public class Entity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual LargeBlob LargeBlob { get; set; }
}
public class LargeBlob
{
public int Id { get; set; }
public virtual byte[] Blob { get; set; }
}
That did work in so far as lazy loading was concerned, but I tried all manner of inverse relationship / foreign key tags, HasOne, OwnsOne, OnDelete(Cascade) in all kinds of combinations, but I couldn't achieve what I wanted to achieve. Just to recap, that would be:
Blob is loaded only when the LargeBlob property is actually derefenced.
If entity.LargeBlob property gets set to a new LargeBlob, the (now "orphaned" ) old LargeBlob gets deleted from the database.
If the entity gets deleted, the related large blob gets deleted.
Quick Update re: versions &c
Note: I'm using VS 2017 15.6.2, .net core 2.0, with EF core 2.1 (to get at least the possibility of some lazy loading). Nuget packages:
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="2.1.0-preview1-final" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.0-preview1-final" PrivateAssets="All" />
I tried splitting into 2 tables with a 1->[0|1] relationship as follows
But by putting the FK in the Entity you actually did the opposite - [0|1]->1 relationship.
To get the desired relationship, the FK must be at LargeBlog. It could be a separate property (column), but the most appropriate is to use the Id property as both PK and FK (the so called shared PK association). You can do it with the following fluent configuration:
modelBuilder.Entity<Entity>()
.HasOne(e => e.LargeBlob)
.WithOne()
.HasForeignKey<LargeBlob>(e => e.Id);
Once you do that, since the whole purpose of doing it was to get separate controllable (eager, explicit or lazy when available) load behavior, it can be seen that the separate table is not really needed - the "entity" containing the blob data can be embedded inside the same table using the table splitting which is achieved by simply adding the following to the above configuration:
modelBuilder.Entity<Entity>().ToTable("MyTable");
modelBuilder.Entity<LargeBlob>().ToTable("MyTable");
Note that while the most logical choice seems to be owned type, unfortunately currently owned types are always loaded (similar to EF6 complex types), so they cannot be used to achieve controllable load behavior.
You should only select the columns you need to save bandwidth:
var entity = await dbContext.Entities
.Where(...)
.Select(e => new
{
Id = e.Id,
Name = e.Name,
LargeBlob = null,
})
.FirstOrDefaultAsync();
and whenever you really need the LargeBlob column, load it manually
entity.LargeBlob = await dbContext.Entities
.Where(e => e.Id == entity.Id)
.Select(e => e.LargeBlob)
.SingleOrDefaultAsync();
You can delete an entity without loading the whole entity, just the Id (and the concurrency token, if present on the entity) suffices
var entity = new Entity { Id = removeEntityId };
dbContext.Entities.Remove(entity);
await dbContext.SaveChangesAsync();
This question was asked here 4 years ago: EF Mapping to prefix all column names within a table I'm hoping there's better handling these days.
I'm using EF6 Fluent API, what I'll call Code First Without Migrations. I have POCOs for my models, and the majority of my database column names are defined as [SingularTableName]Field (e.g., CustomerAddress db column maps to Address field in Customers POCO)
Table:
CREATE TABLE dbo.Customers (
-- ID, timestamps, etc.
CustomerName NVARCHAR(50),
CustomerAddress NVARCHAR(50)
-- etc.
);
Model:
public class Customer
{
// id, timestamp, etc
public string Name {get;set;}
public string Address {get;set;}
}
ModelBuilder:
modelBuilder<Customer>()
.Property(x => x.Name).HasColumnName("CustomerName");
modelBuilder<Customer>()
.Property(x => x.Address).HasColumnName("CustomerAddress");
Goal:
What I'd really like is to be able to say something like this for the FluentAPI:
modelBuilder<Customer>().ColumnPrefix("Customer");
// handle only unconventional field names here
// instead of having to map out column names for every column
With model-based code-first conventions this has become very simple. Just create a class that implements IStoreModelConvention ...
class PrefixConvention : IStoreModelConvention<EdmProperty>
{
public void Apply(EdmProperty property, DbModel model)
{
property.Name = property.DeclaringType.Name + property.Name;
}
}
... and add it to the conventions in OnModelCreating:
modelBuilder.Conventions.Add(new PrefixConvention());
If you have database tables with relationships as follows:
Person
PersonId (PK), Name
PersonDoesService
PersonId (FK), ServiceId (FK)
Service
ServiceId (PK), Type
Is it possible with Entity Framework in one statement to get all Persons that does one service based on serviceId?
The code below will do what you are asking. Make sure that your ServiceId exists.
var people = context.Service.First(s => s.ServiceId == theServiceIdIWant).Person // IEnumerable<Person>
EF 4.3.1. I have defined User and Box entities. Each box may or may not be assigned to a user.
What I'd like to achieve is to have a OwnBox property in User class, and an Owner property in Box class.
in Database, I have defined OwnerId foreignkey in Boxes (Boxes.OwnerId has relation with Users.UserId).
To define the relationship with fluent api, I have defined the following classes:
public partial class User
{
public int UserId {get; set;}
public virtual Box OwnBox { get; set; }
}
public partial class Box
{
public int? OwnerId { get; set; }
public virtual User User { get; set; }
}
Then in my Mapping class for Box, I have defined the relations as follows:
this.HasOptional(t => t.User).WithOptionalDependent(d => d.OwnBox).
Map(m => m.MapKey("OwnerId")).WillCascadeOnDelete(true);
But by firing up the project, I got the error:
Schema specified is not valid. Errors: (56,6) : error 0019: Each
property name in a type must be unique. Property name 'OwnerId' was
already defined.
So I had to tell EF to forget about the OwnerId column first:
this.Ignore(t => t.OwnerId);
Now the project works fine. But I'm still doubtful if this is a good approach and will everything work fine on CRUD operations with foreign key associations.
First of all, this is not one-to-one relationship. In one-to-one relationship the foreign key must be a primary key.
I believe in your scenario the situation can happen:
User = { UserID = 2 }
Box1 = { UserID = 2 }
Box2 = { UserID = 2 }
Nothing stops you from doing that, but which box should be returned when you do that:
User.OwnBox, Box1 or Box2?
EF can deal with that using Independent Association. It will create foreign key, hidden from your POCO class. You can specify the name of the column using MapKey as you did. However, because you also created a property called OnwerID, just as the column used with MapKey, the EF has a problem as two properties are mapped to the same column.
When you use ignore, the POCO OwnerID property is ignored by EF so that fixes the problem of two properties, however, the OwnderID value never gets saved or read to the database. Because EF just ignores it.
Thanks for your question, I have learnt a lot thanks to this.
Given this:
create table Location(
LocationId int identity(1,1) not null primary key,
Address nvarchar(max) not null,
City nvarchar(max) null,
State nvarchar(max) not null,
ZipCode nvarchar(max) not null
);
create table Park(
ParkId int not null primary key references Location(LocationId),
Name nvarchar(max) not null
);
I tried this mapping:
modelBuilder.Entity<Location>();
modelBuilder.Entity<Park>().ToTable("Park");
modelBuilder.Entity<Park>().Property(x => x.LocationId).HasColumnName("ParkId");
Unfortunately that didn't work.
using (var db = new Ef())
{
var park = new Park { Name = "11th Street Park", Address = "801 11th Street", City = "Aledo", State = "TX", ZipCode = "76106" };
db.Set<Location>().Add(park);
db.SaveChanges();
}
It has this error:
The property 'LocationId' is not a declared property on type 'Park'.
Verify that the property has not been explicitly excluded from the
model by using the Ignore method or NotMappedAttribute data
annotation. Make sure that it is a valid primitive property.
How should I map Park entity so its LocationId property fall to ParkId column?
I have this mapping by the way:
public class Location
{
public virtual int LocationId { get; set; }
public virtual string Address { get; set; }
public virtual string City { get; set; }
public virtual string State { get; set; }
public virtual string ZipCode { get; set; }
}
public class Park : Location
{
public virtual string Name { get; set; }
}
If it could help, this is possible in EF 4.0 (via designer), just followed the steps in Chapter 2-11 of Entity Framework 4.0 Recipes, Problem Solution Approach. Now I'm trying it on code first via EF 4.1
[EDIT]
If I change the ParkId to LocationId, things are ok. However, with designer approach, it is possible to map the LocationId to ParkId of table Park; I want to achieve the same thing with code first
create table Park(
LocationId int not null primary key references Location(LocationId),
Name nvarchar(max) not null
);
As I know (and I tried it multiple times) code first doesn't support this => your derived type should use same column names for primary key.
This problem can be described very simply: Current fluent mapping implementation doesn't allow overriding mapping rules from parent entity => parent entity defines names of primary key columns in all derived entities.
IMO the most probable reason is that it was really designed as code first where you don't have existing database and you do not have to bother with database naming - it was up to EF to define names as it needed. Once DbContext API was released people started to use it with existing database massively. But here comes a problem: Initial use cases didn't count with this so some scenarios which are pretty easily done in EDMX are not possible. This is one of them.
Here is a workaround for this issue:
Create a view for the derived table and map your entity class that view. Rename the key column in your view so that it matches the key column in the base table.
eg:
base table User (UserID, FirstName, LastName)
derived table Manager (ManagerID, DepartmentID)
Entity Framework fails to update Manager as the key column is different!
solution:
create view UserManager
as
select
ManagerID as UserID,
DepartmentID
from Manager
Then map the Manager class to the UserManager view, instead of to the Manager table.