I have some tables to specify standard email messages and the addresses from which they should be sent:
Email.Message
-------------
MessageId
Name
FromAddressId
Email.Address
-------------
AddressId
These tables are modeled in code using these classes:
public class EmailAddress
{
public int AddressId { get; set; }
}
public class EmailMessage
{
public int MessageId { get; set; }
public string Name { get; set; }
public int FromAddressId { get; set; }
public virtual EmailAddress FromAddress { get; set; }
}
var message = Db.EmailMessages
.Where(m => m.Name == name)
.Include(m => m.FromAddress)
.SingleOrDefault();
I am using Fluent API to specify the relationship:
internal class EmailMessageMap : EntityTypeConfiguration<EmailMessage>
{
public EmailMessageMap()
{
ToTable("Message", "Email");
HasKey(p => p.MessageId);
// What do I put here?
}
}
How do I set up the join to use the FromAddressId of my EmailMessage class? I've tried these options:
HasRequired(p => p.FromAddress).WithRequiredPrincipal(); // uses LEFT OUTER JOIN
HasRequired(p => p.FromAddress).WithRequiredDependent(); // uses INNER JOIN
These cause the SQL that gets created to use either a LEFT OUTER JOIN or an INNER JOIN, but they always try to join Message.MessageId to Address.AddressId, which is incorrect. How do I make it use the correct column (FromAddressId)? I need to have something to replace the // What do I put here?, because otherwise I get the following exception:
Invalid column name 'FromAddress_AddressId'
EDIT: I can place a [ForeignKey("FromAddressId")] on top of the FromAddress property and it works:
[Extent1].[FromAddressId] = [Extent2].[AddressId]
Progress! Now, how do I do this in Fluent API?
Now, how do I do this in Fluent API?
HasRequired(p => p.FromAddress)
.WithMany()
.HasForeignKey(p => p.FromAddressId);
Related
I have 2 tables in my ASP.NET MVC project: store and product.
The relationship between these tables is many-to-many, so I also have a table StoreProduct.
I want to do CRUD operations on table product, but I need column StoreID from table StoreProduct.
How can I get the storeid from StoreProduct to do CRUD operation on product table?
here is how you'd do it if you want a many-to-many relationship with enums i.e. you want many user roles and work with enums moreover, you can't store it as a flag because you need to know about the roles/privileges without source code(on db side).
TLDR ;) You'd have to create a join table which contains about about who has what privilege(or roles if you want).
There is a Users table which has a list of privileges, a privilege table which has privilege definition i.e. Id, name. And a Join table which will have User and Privilege as it's key. If an entry against this user/privilege combination is present that means this user has this privilege/role.
The code:
//for enum
public enum UserPrivilegeId : int
{
AddProject = 0,
ModifyProject = 1,
DeleteProject = 2,
AddUser = 3,
ModifyUser = 4,
DeleteUser = 5
}
//User class
public record User
{
public User()
{
Privileges = new HashSet<Privilege>();
}
public int Id { get; set; }
public string Username { get; set; }
public string PasswordHash { get; set; }
public virtual ICollection<Privilege> Privileges { get; set; }
public virtual List<UserPrivilege> UserPrivileges { get; set; }
}
//Privilege Class
public record Privilege //note record is IMPORTANT here, because this forces it to compare by value, if you want to *use a class*, then make sure to override GetHashCode and Equals
{
public Privilege()
{
Users = new HashSet<User>();
}
public Privilege(UserPrivilegeId privilegeId, string privilegeName)
{
PrivilegeId = privilegeId;
PrivilegeName = privilegeName;
Users = new HashSet<User>();
}
[Key]
public UserPrivilegeId PrivilegeId { get; set; }
public string PrivilegeName { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual List<UserPrivilege> UserPrivileges { get; set; }
}
//and finally the UserPrivilege join class
public record UserPrivilege
{
public UserPrivilegeId PrivilageId { get; set; }
public Privilege Privilage { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
//The set-up in dbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Privilege>()
.HasKey(p => p.PrivilegeId);
modelBuilder.Entity<Privilege>()
.Property(p => p.PrivilegeId)
.HasConversion<int>();
modelBuilder.Entity<User>()
.HasMany(user => user.Privileges)
.WithMany(privilege => privilege.Users)
.UsingEntity<UserPrivilege>(
j => j
.HasOne(up => up.Privilage)
.WithMany(u => u.UserPrivileges)
.HasForeignKey(up => up.PrivilageId),
j => j
.HasOne(up => up.User)
.WithMany(p => p.UserPrivileges)
.HasForeignKey(up => up.UserId),
j =>
{
j.Property(u => u.PrivilageId).HasConversion<int>();
j.HasKey(u => new { u.PrivilageId, u.UserId });
});
//this adds definitions of privileges to the table
modelBuilder.Entity<Privilege>()
.HasData(
Enum.GetValues(typeof(UserPrivilegeId))
.Cast<UserPrivilegeId>()
.Select(p => new Privilege(p, p.ToString())));
base.OnModelCreating(modelBuilder);
}
Use it by creating a wrapper around it with a boolean on IsActive like this:
public class UserPrivelegesDTO
{
public UserPrivelegesDTO(UserPrivilegeId privilege, bool isActive)
{
this.PrivilegeId = privilege;
this.PrivilegeName = privilege.ToString();
this.IsActive = isActive;
}
public UserPrivilegeId PrivilegeId { get; set; }
public string PrivilegeName { get; set; }
public bool IsActive { get; set; }
}
If you want to convert from List<Privileges> to List<UserPrivilegeDTO>, you can
return await _context.Privileges.OrderBy(x => x.PrivilegeId).ToListAsync(cancellationToken);
To Convert back to List<Privileges>, simply
var privileges = _userPrivilegesViewModel.Privileges.Where(x => x.IsActive).Select(x => new Privilege(x.PrivilegeId, x.PrivilegeName));
If you want to check if the user has privilege
var user = _context.Users.Include(x => x.Privileges).FirstAsync(x => x.Id == 1);
if (request.Editor.Privileges.Any(p => p.PrivilegeId == UserPrivilegeId.ModifyUser))
return true;
When you want to update privileges
var PrivilegeChangeUser = await
_context.Users
.Include(user => user.Privileges)
.Include(user => user.UserPrivileges)
.FirstOrDefaultAsync(user => user.Id == request.UserId);
//**NOTE**you *need* to include the join table i.e. UserPrivileges in order replace the privileges, if you do not include it EF will try to add the privileges which already exist :(
//To update the privileges from an IEnumerable<UserPrivilegeIdEnum>
//first get the privileges objects and add that to users
var AllPrivileges =
await _context.Privileges
.Include(x => x.UserPrivileges)
.Include(x => x.Users)
.Where(x =>
request.Privileges
.Contains(x.PrivilegeId)
).ToListAsync(cancellationToken);
PrivilegeChangeUser.Privileges = AllPrivileges;
I have used an enum as the primary key, but you can of course just use a standard int too. For more information read this about how to configure a many-to-many relationship in efcore
I have two tables. Parent table and link table that stores ids. Parent table have related data from itself. Here is Models:
public class People
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Relation> Relations { get; set; }
}
public class Relation
{
public int Id { get; set; }
public int ParentPeopleID { get; set; }
public int ChildPeopleID { get; set; }
public People People { get; set; }
}
Some test data
And mapped them like this
HasRequired(p => p.People).WithMany(p => p.Relations).HasForeignKey(p => p.ParentPeopleID);
When I'm calling
var Peoplelist = MyDbContext.People.Include(p=>p.Relations.Select(r=>r.People)).Where(p=>p.Id==1).ToList();
It returns itself not related People. In my case it should returns People with ids: 2,3,4 but returns three People with id 1
I can get what I need by MyDbContext.Relation but need by MyDbContext.People
What i'm doing wrong?
Is there another way to do that?
Ivan is correct. You should use join keyword to join the both tables as below. You will get the desired result (People with ids: 2,3,4) by using this query:
var Peoplelist = MyDbContext.Peoples.Join(MyDbContext.Relations, p => p.Id, r => r.ChildPeopleID,
(p, r) => new {p, r})
.Where(j => j.r.ParentPeopleID == 1).ToList();
Hope this will help.
This is what i was trying to achieve but entity framework didn't let me do this:
modelBuilder.Entity<ApplicationUser>()
.HasMany(u => u.Following)
.WithMany(u => u.Followers)
.Map(m =>
{
m.ToTable("FollowTables");
m.MapLeftKey("UserId");
m.MapRightKey("FollowId");
});
But if i use this code then in database, table is created but with no primary key in it.only two column got created.
If i use this code then proper table is created with primary key too in it.
public class FollowTable
{
[Key]
public int autoId { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public ApplicationUser User { get; set; }
[ForeignKey("Follow")]
public int? FollowId { get; set; }
public ApplicationUser Follow { get; set; }
}
here, autoId is primary key and UserId and FollowId both are foreign key to ApplicationUser class where UserId is user's own id and FollowId are the ids which user is following.
autoId UserId FollowId
1 4 11
2 4 12
3 4 13
but my ApplicationUser class structure is something like this :
public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public string Address { get; set; }
public ApplicationUser()
{
this.Followers = new HashSet<ApplicationUser>();
this.Following = new HashSet<ApplicationUser>();
}
// calculated properties
public ICollection<ApplicationUser> Following { get; set; }
public ICollection<ApplicationUser> Followers { get; set; }
i want to use these collections badly.
But this code i think is creating a new table in database named dbo.ApplicationUserApplicationUsers with columns ApplicationUser_Id and ApplicationUser_Id1.
i dont want this table.also my code is too entering data into this table like this follow function:
public virtual bool Follows(int userId, int accountId)
{
var targetUser = _context.Users
.Where(u => u.Id == accountId && u.Id != userId)
.Select(u => new
{
User = u,
CanFollow = u.Followers.All(f => f.Id != userId)
})
.FirstOrDefault();
if (targetUser == null || !targetUser.CanFollow) return false;
var currentUser = _context.Users
.Include(u => u.Following)
.FirstOrDefault(u => u.Id == userId);
if (currentUser == null) return false;
if (currentUser.Following.All(u => u.Id != accountId))
{
currentUser.Following.Add(targetUser.User);
_context.SaveChanges();
return true;
}
return false;
}
Now, suggest me how to configure mapping with or without fluent api.i want to enter data in follow table not in table which was auto generated.
You're conflating two separate relationships. If you were dealing with something like "Friends", then that's a straight M2M: by virtue of me being your friend, you are also my friend. However, a user could follow many people and be followed by none, that means you need two separate relationships to to track that.
public class ApplicationUser
{
...
public virtual ICollection<ApplicationUser> Following { get; set; }
public virtual ICollection<ApplicationUser> Followers { get; set; }
public class Mapping : EntityTypeConfiguration<ApplicationUser>
{
HasMany(m => m.Following).WithMany();
HasMany(m => m.Followers).WithMany();
}
}
Then in your context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ApplicationUser.Mapping());
}
Entity Framework will handle the join tables. You don't need to worry about that, unless you need an M2M with a payload, like the date someone started following or the user started following someone else.
I need to map a many-to-many relationship using Entity Framework Code First. Its a standard socialnetworking FriendRequests mapping. A User Object has a Collection of FriendRequests of type List<User>. In the database I'm using a join table for the relationship as follows:
CREATE TABLE dbo.FriendRequests(
UserId INT NOT NULL FOREIGN KEY REFERENCES dbo.Users(id),
FriendId INT NOT NULL FOREIGN KEY REFERENCES dbo.Users(id),
RequestDate SMALLDATETIME NOT NULL DEFAULT GETUTCDATE())
GO
ALTER TABLE dbo.FriendRequests ADD PRIMARY KEY (UserId,FriendId)
GO
How do I map the user object in Entity Framework Code First to enable a Collection via a join table?
You can try it this way:
public class User
{
public int Id { get; set; }
public ICollection<FriendRequest> FriendRequests { get; set; }
}
public class FriendRequest
{
public int UserId { get; set; }
public int FriendId { get; set; }
public DateTime RequestDate { get; set; }
public User User { get; set; }
public User Friend { get; set; }
}
Mapping with Fluent API:
modelBuilder.Entity<User>()
.HasMany(u => u.FriendRequests)
.WithRequired(f => f.User)
.HasForeignKey(f => f.UserId);
modelBuilder.Entity<FriendRequest>()
.HasKey(f => new { f.UserId, f.FriendId });
modelBuilder.Entity<FriendRequest>()
.HasRequired(f => f.Friend)
.WithMany()
.HasForeignKey(f => f.FriendId);
Because of the RequestDate property in the link table you cannot map this as a many-to-many relationship. Instead you need two one-to-many relationships as shown above.
Possibly you need to disable cascading delete, I am not sure. You can do this by appending .WillCascadeOnDelete(false) at the end of the two one-to-many mappings.
Edit
To your comment: If you remove the RequestDate column you can create your model with a many-to-many relationship. You don't need the FriendRequest entity in this case:
public class User
{
public int Id { get; set; }
public ICollection<User> FriendRequests { get; set; }
}
Mapping with Fluent API:
modelBuilder.Entity<User>()
.HasMany(u => u.FriendRequests)
.WithMany()
.Map(m =>
{
m.ToTable("FriendRequests");
m.MapLeftKey("UserId");
m.MapRightKey("FriendId");
});
I'm trying to apply a filter (ApplyFilter) on a column that is Join (and projected) from another table. I have the following entity:
public class User
{
public virtual int Id { get; private set; }
public virtual string EMail { get; set; }
...
public virtual bool IsActive { get; set; }
public virtual int CompanyId { get; set; }
}
With a UserMap:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("Users");
Id(x => x.Id, "UserId");
Map(x => x.EMail);
...
Join("CompanyUser", r =>
{
r.KeyColumn("UserId");
r.Map(x => x.IsActive);
r.Map(x => x.CompanyId);
r.Fetch.Join();
});
ApplyFilter<CompanyFilter>("this_1_.CompanyId = :companyId");
}
This actually works right now, but as you can see I'm having to include the alias name "this_1_" for the CompanyUser table in the filter... this doesn't sound correct, but if I leave the filter as is defined in the FilterDefinition class I get a Sql with a:
where this.CompanyId = ?p0
which is not mapped 'cos the CompanyId column comes from a different projection (CompanyUser as this_1_)
Is there a way to correct this and let nhibernate figure out the correct alias for the filter?
Thanks in advance for any help.
I think you must apply the filter on the join:
Join("CompanyUser", r =>
{
r.KeyColumn("UserId");
r.Map(x => x.IsActive);
r.Map(x => x.CompanyId);
r.Fetch.Join();
}).ApplyFilter<CompanyFilter>("CompanyId = :companyId");