I am trying to create and associate the entities in my project. The User can have many Roles, Claims and Logins. On the other hand, A claim or login can have only one user, while a role can also be with many user. I have the relationship defined with fluent API:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
modelBuilder.Entity<User>().HasMany(u => u.Claims).WithRequired(cl => cl.User).Map(m => m.MapKey("User"));
modelBuilder.Entity<Claim>().HasRequired(cl => cl.User).WithMany(u => u.Claims).Map(m => m.MapKey("User"));
modelBuilder.Entity<User>().HasMany(u => u.Logins).WithRequired(lg => lg.User).Map(m => m.MapKey("User"));
modelBuilder.Entity<Login>().HasRequired(lg => lg.User).WithMany(u => u.Logins).Map(m => m.MapKey("User"));
modelBuilder.Entity<User>().HasMany(u => u.Roles).WithMany(ro => ro.Users).Map(userRoles =>
{
userRoles.ToTable("Users_Roles");
userRoles.MapLeftKey("User");
userRoles.MapRightKey("Role");
});
modelBuilder.Entity<Role>().HasMany(ro => ro.Users).WithMany(u => u.Roles).Map(userRoles =>
{
userRoles.ToTable("Users_Roles");
userRoles.MapLeftKey("User");
userRoles.MapRightKey("Role");
});
}
As you see, my database tables do not follow Entity Framework's convention, instead of using 'User_Id' as foreign key, the foreign key is simply named 'User'. However, I keep getting this 'Invalid Column Name: User_Id' exception message.
I tried to define the foreign key column name with the above code by calling the methods Map() and MapKey(), but with no prevail. Why is this happening? Am I writing the mapping code wrong? Can someone help?
PS: The exception error message is very unhelpful that I do not know which table this column name error is associated with. Does anyone know how to make the exception message show what the table name is, not just the column name? Thanks.
Also I've added code I used for Claim, Login and Role entities(with unrelated methods removed).
public class Claim
{
public string Id { get; protected set; }
public string Type { get; protected set; }
public string Value { get; protected set; }
public virtual User User { get; protected set; }
public Claim() { }
public Claim(string id, User user, string type, string value)
{
Id = id;
User = user;
Type = type;
Value = value;
}
}
public class Login
{
public string Id { get; protected set; }
public string Provider { get; protected set; }
public string Key { get; protected set; }
public DateTime? DateLoggedin { get; protected set; }
public virtual User User { get; protected set; }
public Login() { }
public Login(string id, string provider, string key, DateTime? dateLoggedIn = null)
{
Id = id;
Provider = provider;
Key = key;
DateLoggedin = dateLoggedIn;
}
}
public class Role
{
public string Id { get; protected set; }
public string Title { get; protected set; }
public string Description { get; protected set; }
public bool IsAdmin { get; protected set; }
public bool IsBanned { get; protected set; }
public IList<User> Users { get; protected set; }
public Role() { }
public Role(string id, string title, string description, bool isAdmin, bool isBanned)
{
Id = id;
Title = title;
Description = description;
IsAdmin = isAdmin;
IsBanned = isBanned;
}
}
The User_Id column is being created by EF convention because you map your key to a property by the same name as your navigation proeprty.
You could either remove the .Map() part and let EF handle the key, or define a key property on your Claim and Login classes, and map that in the fluent api. I would personally do the latter.
E.g:
public class Claim
{
public string Id { get; protected set; }
public string Type { get; protected set; }
public string Value { get; protected set; }
public virtual User User { get; protected set; }
public int UserId{get;set;}
public Claim() { }
}
And in your ModelCreating
modelBuilder.Entity<User>().HasMany(a=>a.Claims).WithRequired(a=>a.User).HasForeignKey(a=>a.UserId);
If you create a key field of 'EntityNameId' (UserId, ClaimId, etc) then EF convention will automatically map that as your Foreign Key, without the fluent mapping. However, if you name is something else (UsernameId, or whatever), you have to explicitly provide the mapping. Some reading on that.
Related
I'm trying to create an ApplicationUser which has a User as a child object, this is what the models look like:
ApplicationUser:
public class ApplicationUser : IdentityUser
{
public User User { get; set; }
}
User:
public class User
{
public int Id { get; set; }
[ForeignKey("AspNetUser")]
public string AspNetUserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ApplicationUser AspNetUser { get; set; }
}
Within my DbContext I have:
public class IdentityDbContext : IdentityDbContext<ApplicationUser>
{
public IdentityDbContext(DbContextOptions<IdentityDbContext> options)
: base(options)
{
}
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfiguration(new AdminConfiguration());
}
}
AdminConfiguration:
public class AdminConfiguration : IEntityTypeConfiguration<ApplicationUser>
{
public void Configure(EntityTypeBuilder<ApplicationUser> builder)
{
var id = "bc62cdff-77ca-4473-a467-210eb36fdd5d";
var admin = new ApplicationUser
{
Id = id,
UserName = "admin",
NormalizedUserName = "ADMIN",
Email = "admin#dotvvm.com",
NormalizedEmail = "ADMIN#DOTVVM.COM",
EmailConfirmed = true,
SecurityStamp = new Guid().ToString("D")
};
admin.PasswordHash = GeneratePassword(admin, "Admin12345!");
builder.HasData(admin);
builder.OwnsOne(a => a.User).HasData(new User
{
Id = 1,
AspNetUserId = id,
FirstName = "Test",
LastName = "Test"
});
}
private string GeneratePassword(ApplicationUser user, string password)
{
var passHash = new PasswordHasher<ApplicationUser>();
return passHash.HashPassword(user, password);
}
}
With this code, I create a migration and try to execute Update-Database but I get this error:
To change the IDENTITY property of a column, the column needs to be dropped and recreated
I can't figure out what I'm doing wrong, does anyone know?
I'm almost sure that you're using .OwnsOne wrong (but i doubt it is root cause, i speak about it later)
Owned types are Value objects. Value objects have no identity on their own and exist only as a part of their owner like
//this is entity, it has identity
public class Person
{
public Guid Id { get; set; }
public Name Name { get; set; }
}
//and this is value object and could be owned type
public class Name
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
If you want both ApplicationUser and User to be entities (make sense) you could consider One-to-One relationship betwen them, like this
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ApplicationUser>()
.HasOne(a => a.User)
.WithOne(b => b.ApplicationUser)
.HasForeignKey<ApplicationUser>(b => b.AspNetUserId);
}
and then your
builder.HasData(new User
{
Id = 1,
AspNetUserId = id,
FirstName = "Test",
LastName = "Test"
});
might be valid and ... might not
because another possible source of you problem could be Autoincrement Id field (is it autoincrement in your User class?)
If so -
builder.OwnsOne(a => a.User).HasData(new User
{
Id = 1, //<<---- try removing this
AspNetUserId = id,
FirstName = "Test",
LastName = "Test"
});
this could solve your issue
I am using Entity Framework code first with fluent API I have an items table with foreign keys from users and units tables
but when I load the table to ObservableCollection then bind it to a datagrid the table normal column load it's data normally into the datagrid excpet for the foreign keys which show nothing but when i insert a break point to see the data inside the ObservableCollection I can see that every thing from Users and Units table is there
private void MainContentsWindow_ContentRendered(object sender, EventArgs e)
{
using (var db2 = new DataContext())
{
var AllItems2 = new ObservableCollection<Model.Items.Item>(db2.Items);
ItemsDataGrid.ItemsSource = AllItems2;
}
}
Users
public class User
{
public User()
{
Id = Guid.NewGuid();
IsActive = false;
}
public Guid Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public UserGroup Group { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Items.Item> Items { get; set; } = new List<Items.Item>();
}
public enum UserGroup
{
Administrator = 1,
User,
Unknown
}
base
public class NormalBaseModel : CommonBase
{
public NormalBaseModel()
{
Id = new Guid();
CreateDate = DateTime.Now;
EditDate = null;
}
public Guid Id { get; set; }
public string Notes { get; set; }
public virtual User CreateBy { get; set; }
public DateTimeOffset? CreateDate { get; set; }
public virtual User EditBy { get; set; }
public DateTimeOffset? EditDate { get; set; }
}
items
public class Item : NormalBaseModel
{
public string NameAr { get; set; }
public string NameEn { get; set; }
public int? ManualId { get; set; }
public string Barcode { get; set; }
public byte?[] Image { get; set; }
public virtual Unit Unit { get; set; }
public string MadeIn { get; set; }
public bool IsSerail { get; set; }
public bool IsExpire{ get; set; }
}
Here is a test project on Github
https://github.com/ahmedpiosol/psychic-parakeet.git
https://imgur.com/a/zimd4
When you load your items via EF it needs to create new instances of User and Item. Behind the scenes, EF will call the constructor for each new instance. Your problem is in your constructors:
public User()
{
Id = Guid.NewGuid(); // <- here
}
Your constructor reassigns a new ID each time an instance is created, this will break the referential integrity and cause all sorts of other problems.
Your code doesn't know the difference between creating a new User and recreating a User instance from the database.
I suggest removing the assignments from inside your constructor and placing this either in a static Create method or place wherever you are creating a new User or Item.
p.s. WPF is irrelevant to your problem here.
Fluent API needs to specify foreign key in code, something like
modelBuilder.Entity<Items>()
.HasRequired(o => o.User)
.WithMany(c => c.Items)
.HasForeignKey(o => o.UserId);
So there is a way to map 2 hierarchical classes in EF to the same table with a defining column and value (http://msdn.microsoft.com/en-us/data/jj591617.aspx#2.4). I am wondering if there's a way to map 2 non-hierarchical classes to the same table in the same manner.
Example:
class User
Guid Id
string Name
Guid? GroupId
class Group
Guid Id
string Name
Table
uid Id PK
varchar Name
bit IsGroup
uid GroupId nullable
I can't change the schema of that table, so the only solution I've come up with so far is to create a view for User and a view for Group.
Mo,
I think that at the end of the day you are after a user group that contains the users that belong to it. If that's the case here is something that will get you started. Remember that this is not meant to be a "best practice" just an example to get you up and running. you will need to tweak this code for your exact situation.
Here's the basic jest of it. You need to give group a property of type List...then the populate the list of users from a database query. In the below example I wrote a method that handles populating the list. However in practice you don't have to do populate the list here you could instantiate the userGroup and populate the list of users from inside an action method. Like I said this in not a best practice just a quick example.
Here is an example.
public class userGroup
{
class user
{
private int id { get; set; }
private string name { get; set; }
private Guid grpId { get; set; }
public int userId { get { return id; } set { id = value; } }
public string userName { get { return name; } set { name = value; } }
public Guid groupId { get { return grpId; } set { grpId = value; } }
public user() { }
}
class group
{
private Guid id { get; set; }
private string name { get; set; }
public List<user> usersInGroup = new List<user>();
public Guid groupId { get { return id; } set { id = value; } }
public string groupName { get { return name; } set { name = value; } }
public group() { }
}
public userGroup() { }
public group getUserGroup()
{
group x = new group();
Guid newGroupId = Guid.NewGuid();
x.groupId = newGroupId;
var userQuery = myDB.Where(n => n.myField == myConditon).Select(n => new
{
n.userId,//I'm assuming that the database query returns a field userId
n.userName//I'm assuming that the database query returns a field userName
});
foreach(var user in userQuery)
{
userGroup.user y = new userGroup.user();
y.groupId = newGroupId;
y.userId = user.userId;
y.userName = user.userName;
x.usersInGroup.Add(user);
}
return x;
}
}
I hope that I understood your question and that my example points you in the right direction.
Best wishes,
Bill
You can pretty easily model this by introducing an abstract base type from which both types derive.
public abstract class BaseNamedEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Group : BaseNamedEntity
{
public virtual ICollection<User> Users { get; set; }
}
public class User : BaseNamedEntity
{
public Guid GroupId { get; set; }
public virtual Group Group { get; set; }
}
In the context you tell that IsGroup is the discriminator:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BaseNamedEntity>().ToTable("MyTable");
modelBuilder.Entity<Group>().Map(m => m.Requires("IsGroup")
.HasValue(true));
modelBuilder.Entity<User>().Map(m => m.Requires("IsGroup")
.HasValue(false));
}
public DbSet<BaseNamedEntity> BaseNamedEntities { get; set; }
}
I don't think that BaseNamedEntity is a particularly good name, so maybe you have to come up with something more meaningful.
I need to delete a record of an entity but keep all the records of another entity that is related with it:
Entity record to remove is:
public class Ask
{
// Primary properties
public int Id { get; set; }
public string Title { get; set; }
public string Message { get; set; }
public DateTime DateCreated { get; set; }
}
The related records that I want to keep after deleting an Ask record is of type MessageAsk :
public class Message
{
// Primary properties
public int Id { get; set; }
public string NameFrom { get; set; }
public string EmailFrom { get; set; }
public string TelephoneFrom { get; set; }
public string Title { get; set; }
public string MessageText { get; set; }
public bool? Approved { get; set; }
public DateTime? DateCreated { get; set; }
public DateTime? DateRead { get; set; }
// Navigation properties
public Member MemberFrom { get; set; }
public Member MemberTo { get; set; }
public MessageType MessageType { get; set; }
public Message MessageParent { get; set; }
}
public class MessageAsk : Message
{
public Ask Ask { get; set; }
}
Resuming, I want to delete an Ask and keep all related MessageAsk's.
EDIT:
I use the service Delete:
private readonly IRepository<Ask> _askRepository;
private readonly IRepository<MessageAsk> _messageAskRepository;
public bool Delete(int askId)
{
try
{
Ask askToDelete = _askRepository.GetById(askId);
IList<MessageAsk> relatedMessageAsks = _messageAskRepository.Query.Where(m => m.Ask.Id == askId).ToList();
_askRepository.Delete(askToDelete);
_askRepository.Save();
}
catch
{
return false;
}
return true;
}
And I use a repository to Delete the Entity:
public class Repository<T> : IRepository<T> where T : class
{
protected DbContext _dataContext;
protected DbSet<T> _dbSet;
public Repository(DbContext context)
{
_dataContext = context;
_dbSet = _dataContext.Set<T>();
}
public T NewEntityInstance()
{
return _dbSet.Create();
}
public void Delete(T entity)
{
if (_dataContext.Entry(entity).State == EntityState.Detached)
{
_dbSet.Attach(entity);
}
_dbSet.Remove(entity);
}
public virtual void Delete(object id)
{
T entity = _dbSet.Find(id);
Delete(entity);
}
public T GetById(int id)
{
return _dbSet.Find(id);
}
public virtual IQueryable<T> Query
{
get
{
return _dbSet.AsNoTracking(); <------ SOURCE OF THE PROBLEM - I HAD TO REMOVE THE ASNOTRACKING OPTION TO SOLVE THE PROBLEM
}
}
}
Error I get now:
"The DELETE statement conflicted with the REFERENCE constraint "FK_Messages_Asks". The conflict occurred in database "Heelp", table "dbo.Messages", column 'Ask_Id'.
Thanks
If your relationship is optional (that is, the foreign key from MessageAsk table to Ask table allows NULL values), you can do it this way:
using (var context = new MyContext())
{
var askToDelete = context.Asks.Single(a => a.Id == askToDeleteId);
var relatedMessageAsks = context.MessageAsks
.Where(m => m.Ask.Id == askToDeleteId)
.ToList();
// or just: context.MessageAsks.Where(m => m.Ask.Id == askToDeleteId).Load();
context.Asks.Remove(askToDelete);
// or DeleteObject if you use ObjectContext
context.SaveChanges();
}
(or context.Messages.OfType<MessageAsk>()... instead of context.MessageAsks... if you don't have a set for the derived type in your context)
You don't need to set the MessageAsk.Ask property to null explicitly here. EF will do that automatically when the askToDelete is removed and update the MessageAsk with FK = NULL in the database.
It does not work if the relationship is required (no NULLs for the FK are allowed) as you would violate a referential foreign key constraint in the database when the principal (askToDelete) would be deleted. In that case you need to assign the relatedMessageAsks to another Ask before you delete the askToDelete.
I have a simple schema that looks like this:
The idea is that there are Users, Departments and Flows. For each Department, a User can "star" multiple Flows to "bookmark" them (therefore Department/User pair can have many Flows).
Ideally, I'd like to use the following classes:
public class User
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
}
public class Department
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual IDictionary<User, IList<StarredFlow>> StarredFlows { get; protected set; }
public Department()
{
this.UserStarredFlows = new Dictionary<User, IList<StarredFlow>>();
}
}
public class Flow
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
protected Flow() { }
}
public class StarredFlow
{
public virtual int Id { get; protected set; }
public virtual User User { get; protected set; }
public virtual Department EntryPoint { get; protected set; }
public virtual Flow Flow { get; protected set; }
public virtual string Name { get; protected set; }
protected StarredFlow() { }
public StarredFlow(User user, Department ep, Flow flow, string name)
{
this.User = user;
this.EntryPoint = ep;
this.Flow = flow;
this.Name = name;
}
}
This would allow pretty literal code:
var userStarredFlowsForDepartment = department.StarredFlows[currentUser];
However, NHibernate doesn't support mapping dictionaries of lists. I found a similar question/answer that involves moving the IList<StarredFlow> to a new class and mapping it using a component. Sadly, FluentNHibernate doesn't support doing collection mapping with components (there's no HasMany off of the component mapping).
This seems like a trivial thing to do with an NHibernate, but I'm struggling to find a solution. Is there a better solution to doing this? I'd really like to not have to resolve to just using session.Save(starredFlow) and Query<StarredFlow>() because they move the logic outside of the models.
What you want to do is definitely not supported. I'm not saying it wouldn't be a theoretically valid design, but NHibernate does not support it and it's unlikely that it will do so in the future.
That said, you can always have a method in Department instead of a dictionary, taking an IQueryable:
public IEnumerable<StarredFlow> GetUserStarredFlows(IQueryable<StarredFlow> source,
User user)
{
return source.Where(x => x.User == user)
.ToList(); //you could skip this depending on your usage
}
Or (a bit more complex) you could inject the IQueryable or the session.