Entity Framework Mapping to Lookup table - entity-framework

I have 3 tables that need to be mapped with Entity Framework and I'm not sure the proper way to go about this. Here are my 3 entities:
public class User
{
[Key]
public int user_id {get; set;}
public string user_name {get; set;}
public virtual List<Role> roles {get; set;}
}
public class Role
{
[Key]
public int role_id {get; set;}
public string role_name {get; set;}
}
public class User_Role
{
[Key]
public int user_role_id {get; set;}
public int user_id {get; set;}
public int role_id {get; set;}
}
Please note that the User_Role entity just represents a lookup table to link many roles to a single user.
With SQL I would just do something like:
SELECT User.user_name, Role.role_name FROM User INNER JOIN User_Role ON User_Role.user_id = User.user_id INNER JOIN Role ON Role.role_id = User_Role.role_id WHERE User.user_id = 123
I am relatively new to Entity Framework so I'm not sure the best way to tackle this using EF4 DbContext (and possibly Fluent API?) but I'm hoping its pretty straight forward.
Thanks in advance.

It turns out I needed to use Fluent API to map a many to many table (User_Role).
modelBuilder.Entity<Role>()
.HasMany<User>(u => u.users)
.WithMany(r => r.roles)
.Map(m =>
m.MapLeftKey("role_id")
m.MapRightKey("user_id")
m.ToTable("User_Role"));

Related

EF Core one-to-one relationship with string property as key

I am using entity framework core 5 query syntax. There are 3 classes which reflect the tables in database.
public class Record { public int RecordID {get; set;} public decimal Amount {get; set} public string AccountName {get; set;} }
public class Dimension { public int DimensionID {get; set;} public string DimensionSymbol {get; set;} }
public class DimensionName { public int ID {get; set;} public string Name {get; set;} }
public class RecordDto { public int ID {get; set;} public string DimensionName {get; set;} }
The aim is to find DimensionName foreach Record. The relationship is as follows. RecordID field is a foreign key for Dimension. So it is easy to find this relationship. The problem is with DimensionName. DimensionID is NOT a foreign key to DimensionName. The logical key is DimensionSymbol which sometimes is a simple text like "200.000B" and sometimes it represents the number like 190. If DimensionSymbol is 190 then this 190 reflects the ID in DimensionName.
My query is as follows.
var query = from r in context.Records
join d in context.Dimensions on r.RecordID equals d.DimensionID
join n in context.DimensionNames on int.Parse(d.DimensionSymbol)\>0 ? int.Parse(d.DimensionSymbol) : 0 equals n.ID
select new RecordDto
{
ID = r.RecordID,
DimensionName = n.Name
}
And it doesn't work. Could you please adjust the code so that it works? Maybe I should configure sth in OnConfiguring method.

Entity Framework - many to many with one primary

I have a Channel domain model as:
public class Channel
{
public string Id {get; set;}
public List<ChannelUser> ChannelUsers {get; set;}
}
and a ChannelUser class as follows (I have additional meta data on the table, removed here for brevity, which is why I used a specfic class to represent the many-to-many relationship).
public class ChannelUser
{
public string ChannelId {get; set;}
public string UserId {get; set;}
}
The primary key for ChannelUser is (ChannelId, UserId).
One (and only one) of these ChannelUsers can be the Owner of the Channel.
Theoretically, a User can be the Owner of many channels (but obviously a ChannelUser can only be the owner of the channel it is associated to).
I want to enforce that at database level rather than having a "IsOwner" property on ChannelUser and using business logic to ensure that the flag is only set once per channel. I want to also enforce that the ChannelOwner is one of the ChannelUsers and therefore don't want a relationship directly from Channel > User but keep it as Channel > ChannelUser.
Therefore I have updated Channel class as follows:
public class Channel
{
public string Id {get; set;}
public List<ChannelUser> {get; set;}
public ChannelUser Owner {get; set;}
}
And added the following fluent API expressions:
modelBuilder.Entity<ChannelUser>().HasKey(cu => new { cu.ChannelId, cu.UserId });
modelBuilder.Entity<ChannelUser>().HasOne<Channel>(x => x.Channel).WithMany(x => x.ChannelUsers)
.OnDelete(DeleteBehavior.NoAction);
modelBuilder.Entity<ChannelUser>().HasOne<User>(x => x.User).WithMany(x => x.UserChannels)
.OnDelete(DeleteBehavior.NoAction);
When adding migration this results in:
Both relationships between 'ChannelUser.Channel' and 'Channel.ChannelUsers' and between 'ChannelUser' and 'Channel.Owner' could use {'ChannelId'} as the foreign key. To resolve this configure the foreign key properties explicitly on at least one of the relationships.
Which I understand but I think this is what is desired. Because ChannelUser.ChannelId should always be the same Channel.Id for both relationships.
Please can anyone suggest how I can craft this relationship or an alternative approach that still enforces the many to many and the one to one relationships between the tables?
This isn't ideal approach but can be done, have a look at this ER diagram :
Ideal approach would be having proper Entity Mapping like :
User Class :
public class User
{
public string Id {get; set;}
public virtual List<Channel> Channels {get; set;}
// Other properties....
}
Channel Class :
public class Channel
{
public string Id {get; set;}
public List<User> Users {get; set;}
public User Owner {get; set;}
public string? OwnerId { get; set; }
public User Owner { get; set; }
}
ChannelUser Class to join the two:
public class ChannelUser
{
public string Id {get; set;}
public string ChannelId {get; set;}
public string UserId {get; set;}
}
Mapping :
modelBuilder.Entity<User>()
.HasMany<Channel>(user => user.channels)
.WithMany(channel => channel.users)
.Map(cu =>
{
cu.MapLeftKey("UserId");
cu.MapRightKey("ChannelId");
cu.ToTable("ChannelUser");
});
// configure one-to-many relationship for ownership
modelBuilder.Entity<Channel>()
.HasRequired<User>(c => c.User)
.WithMany(u => u.ChannelId )
.HasForeignKey<int>(c => c.UserId);
Responsibility of checking if user exists should be fairly easy in controller code after normalizing this, or we can use DB procedure, trigger to create constrain that checks if given owner ID is indeed in ChannelUser table for given channel.

ForeignKey Attribute in database first application

Following the ForeignKey docs, and multiple examples online I was under the influence that if I give my property (foreign key) this attribute, it would get replaced in a Html.Display call by the first textual property of the parent table.
This doesn't happen and all I get is the same foreign key field.
Does this work in db first applications, and if so, how do I make it work (using ForeignKey)?
Thanks.
EDIT: Or is this Scaffolding exclusive behaviour?
UPDATE: Example code:
// Entity model in Case.cs
public partial class Case
{
public int ID {get; set;}
public string Description {get; set;}
public int Classification_ID {get; set;}
public virtual Classification Classification {get; set;}
}
// Entity model in Classification.cs
// It's a lookup table
public partial class Classification
{
public int ID {get; set;}
public string Label {get; set;}
}
// File with partials
[MetadataType(typeof(CaseMetadata))]
public partial class {}
public class CaseMetadata
{
[ForeignKey("Classification")]
public int Classification_ID {get; set;}
}

How to model more entities to reference same kind of collections within single table

I'm not very experienced with EF and I'm trying to figure out what is the proper way or what are the options for creating entity, collection of which can be contained in other entities (different tables).
Let's say I have three existing classes (ModuleA, ModuleB, ModuleD) that I want to contain its own collection of Data entities.
I wanted a single table for Data entities but I don't like the idea of three nullable Guid columns for each FK.
That also gives me error on applying migration ("...may cause cycles or multiple cascade paths") - which could by probably solved by removing cascade delete and deleting Data manually, but I don't like that idea.
What I would like most is the single (shared) Guid property on Data for FKs to all three modules, which is not possible at least not without same ID existing in all three main tables at the same time (since it creates three FKs in DB).
class Data
{
public int Id {get; set;}
public byte[] Values {get; set;}
}
class ModuleA
{
public Guid Id {get; set;}
public ICollection<Data> Data {get; set;}
// some other stuff...
}
class ModuleB
{
public Guid Id {get; set;}
public ICollection<Data> Data {get; set;}
// ...
}
class ModuleC
{
public Guid Id {get; set;}
public ICollection<Data> Data {get; set;}
// some different other stuff...
}
You can create 3 module-to-data many-to-many tables like this:
class Data
{
public int Id {get; set;}
public byte[] Values {get; set;}
}
class ModuleAData
{
public ModuleAId {get; set;}
public DataId {get; set;}
}
class ModuleA
{
public Guid Id {get; set;}
public ICollection<ModuleAData> Data {get; set;}
// some other stuff...
}

Ef code first one to many relationship

How o define one to many relationship in EF with different primary and foreign key name
UPDATED
Public class Tb1
{
[Key]
public int ID{get; set;} // primary
**public int foreignKey{get; set;} //foreign key**
public string name{get; set;}
[Foreign("foreignKey")]
public virtual ICollection<Tb2> Tb2{ get; set; }
}
Public class Tb2
{
[Key]
public int ID {get; set;} //primary
public int tb1ID {get; set}
public string address {get; set;}
}
Here i want one to many relationsip on Primary key: foreignKey at TB1
foreign kye: tb1ID at TB2
HOW??
My Nomal approach would be to include the navigation property as well. So I would change Tb2 like this:
Public class Tb2
{
[Key]
public int ID {get; set;} //primary
public int Tb1ID {get; set;} //notice I changed case on this variable as well
public Tb1 Tb1 {get; set;} //this is the new variable
public string address {get; set;}
}
Code first should be able to automatically understand the relationship now. If you don't want the Tb1ID property, you can remove it, and it will still work out just fine.