How to map a navigational property to a table where it isn't a key column? - entity-framework

I have an Entity like:
User
Id
Username
public virtual List<String> CountryNames { get;set; }
And it has a property for CountryNames as seen above, and this is linked to another table but it isn't linked on a key column.
The CountryNames property should get all UserCountry rows that have the Username.
UserCountry
Id
CountrName
Username
How can I configure the User model to do this?
I realize this is bad design, I'm not looking for schema design advise at this point as it is out of my hands.
I'm using EF6.

Related

Why collections of owned types generate an Id and how can I avoid that?

As Microsoft says here :
Owned entities are essentially a part of the owner and cannot exist without it, they are conceptually similar to aggregates
It means in DDD architecture (Domain Driven Design) we can use owned types (or collection of owned types ) as an entity inside an aggregation or as a value object. On the other hand, we know that ValueObject in DDD has no Identity value because of its structure and its Immutability. I want to know if I decide to use the Owned type to implement the value object how can I force it to avoid making Id in the creation table?
For example, as you see in the following picture (that Microsoft mentioned here) when we use a collection of owned types, EF makes an "Id" field in the table that no sense in Address Value Object! How to avoid it? and Is it really a correct option?
That example is from the OwnsMany scenario where it clearly explains that it needs a FK in the table to associate addresses back to their Distributor. How else would an Address record associate back to the Distriburor when loading the entities?
If a Distributor only has 0-1 address then you don't need an OwnerId on Address, the Address' Id column would serve as PK and FK back to the Distributor. EF needs a "Key" on each table to uniquely identify each row. You could possibly avoid an "Id" column by mapping a composite key, essentially:
public class Address
{
[Key, Column(0), ForeignKey("Owner")]
public int OwnerId { get; set; }
[Key, Column(1)]
public string Street { get; set; }
[Key, Column(2)]
public string City { get; set; }
public virtual Distributor Owner { get; set; }
}
A dedicated unique, and DB Generated Id column for the Address table IMO makes more sense than a large, composite key of strings and FK.
Ownership as far as the database is concerned is identical to HasOne / HasMany in the way the schema is laid out & relational rules. What differentiates OwnsMany from HasMany is how EF will allow you to access those owned entities. You cannot have a DbSet<Address>, only access Addresses through it's Distributor. It serves no real purpose except to scratch particular design pattern itches. :)

EF FluentAPI how to make navigation property accessible by either object or Id

I have a POCO User that has a Company property.
When create a User in WebAPI I would like to just set the Company_Id that I am getting passed in json in the request and not have to load the Company entity from the database context in order to set it in the User.
I tried adding a User.Company_Id property in addition to the User.Company property and migrations ends up creating a Company_Id1 column in my database.
you should use Forign key attribute to let EF know it's a foreign key.
public class User
{
[ForeignKey("Company_Id")]
public Company Company {get;set;}
public int Company_Id {get;set;}
}

How to represent objects from multiple databases in the EF Mdel / Data Context class

I am working on an intranet app. My data context class has some references to table(s) in other databases.
For example, one of my classes is like this:
public class SomeClass
{
public int Id {get;set;}
public string Name {get;set;}
...
public int EnteredById {get;set;}
}
Here is EnteredById is in fact a foreign key to Employee table in another database (on the same server) and that table is not part of the Data Context class.
I know I cannot create a foreign key on this property and this is fine as long as I can keep it in this class/ table and provide a value for it by going to Employee table in another database and retrieving it from there based on User’s Windows User Name.
How do I handle this situation while Employee table is not part of my Data Context?

EF5 - Navigation properties not loading in a reversed engineered code first model

I'm banging my head on the keyboard with this one, basically what I'm trying to do is rather simple.
I reversed engineered an existing database with the EF Power Tools, I did that mostly because the database in question is quite old and missing a lot of foreign keys, or even primary key for that matter. The goal was to be able to add navigation properties to the generated model, mostly to have a cleaner code when querying the model.
Now I have 2 classes User and CostCentre, mapped to their respective tables Users and CostCentres with proper primary keys, PK of Users is Username named Userid in CostCentres, theoretical relation between those two is 1 to M (one User can have multiple CostCentre).
I added a navigation property to User like so
public virtual ICollection<CostCentre> CostCentres { get; set; }
I'm initializing the list in the default constructor with a simple
public User()
{
CostCentres = new List<CostCentre>();
}
To CostCentre I added a User property like so
public virtual User User { get; set; }
In the generated CostCentreMap class, I mapped the navigation property like so
this.HasRequired(cc => cc.User)
.WithMany(u => u.CostCentres)
.HasForeignKey(cc => cc.Userid);
Now whenever I query db.Users I can't get the property to fill, I know the navigation property kind of work, because when I do
db.Users
.Include(u => u.CostCentres);
The resulting query does the join properly on the proper columns in each table, however if I do
User user = db.Users
.Include("CostCentres")
.SingleOrDefault(u => u.Username == userName);
The property CostCentres stays empty.
I'm pretty sure I'm missing something quite obvious but for the love of Barnaby Jones, I can't get my hands on what.
Any help would be greatly appreciated.

Entity Framework Code First - Table Per Type Inheritance - Insertion Issue?

I'm having an issue inserting an instance of a subclass that inherits from a base class.
Consider the following code snippets from these POCOs:
public abstract class EntityBase<T>
{
private T _id;
[Key]
public T ID
{
// get and set details ommitted.
}
}
public abstract class PersonBase<T> : EntityBase<T>
{
// Details ommited.
}
public class Applicant : PersonBase<int>
{
// Details ommitted for brevity.
}
public class Employee : Applicant {}
Pretty standard inheritance right now. In our system, when an applicant finally becomes an employee, we collect extra data. If not hired, they remain an applicant with a limited set of information.
Now consider the fluent API that sets up the table-per-type inheritance model:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Set up table per type object mapping for the Visitor Hierarchy.
modelBuilder.Entity<Employee>().ToTable("Employees");
}
So far, so good...
If I look at the database this creates, I have a table named Applicants with an Id column of type int, auto-incrementing ID and I have an Employees table with an ID field as the primary key (non auto incrementing).
Basically, the ID field in the Employees table is a foreign key to the Applicants table.
This is what I want. I don't want a record into the Employees table corresponding to the Applicants table until they actually become an Employee.
The problem comes when I try to insert an Employee which comes down to this code:
public void PersistCreationOf(T entity)
{
DataContextFactory.GetDataContext().Set(typeof(T)).Add(entity);
}
The problem: It inserts a brand new applicant and Employee. I hooked it up to the Sql Profiler and looked at both insert queries that come down.
I want to just insert the Employee record with the ID it already has (the foreign key from the Visitors table).
I understand by default it needs to this: Obviously if you create a subclass and insert it, it needs to insert into both tables.
My question is is possible to tell the Framework - the base table already has information - just insert into the child table?
Thanks in advance!
Aside from sending raw SQL commands to insert the Employee minus Applicant properties fragment into the Employees table I believe it's impossible. You can either update or insert an entity. What you want is basically to update the base part of the Employee (or do nothing if nothing changed) and insert the derived part which is not possible.
Imagine what an ORM does: It maps key identities in the database to object identities in memory. Even in memory you couldn't achieve what you want: If you have an object in memory which is a Applicant, it is always an applicant. You cannot magically "upgrade" it to an Employee. You would have to create a new object of type Employee, copy the properties of the Applicant into the base properties of your new Employee and then delete the Applicant. The result is a new object with a new object identity.
I think you have to follow the same procedure in EF. Your Employee will be a new entity with new rows in both Applicant and Employee table and you need to delete the old Applicant. If you have autogenerated keys it will be a new identity with a new ID. (If you hadn't autogenerated keys you could supply the old ID again after deleting the old Applicant, thus "faking" an unchanged identity.) This will of course create big potential trouble if you have references to the old applicant with FK constraints.
Perhaps inheritance is not optimal for this scenario to "upgrade" an applicant into an employee. An optional navigation property (1-to-0...1 relationship) inside of the Applicant which refers to another entity containing the additional properties which make the applicant an employee would solve the problem. This navigation property could be set or not, letting you distinguish between an applicant and applicant which is also an employee. And you would not need to delete and change the ID of the applicant when you make it an employee.
(As said, "I believe". Maybe there is a hidden way, I didn't see.)