How to handle an association table linking the same 2 tables - c#

I am using Entity Framework 4.3 code first using an already existing database.
There is a Users table that has the following columns:
- UserID (int)
- FirstName (string)
- Surname (string)
I have an association table called Impersonations. The Impersonations table is an association table between a user table and the same user table. A user can have a list of users. Here is the Impersonations table structure:
- ImpersonateID (int primary key)
- UserID (int FK to Users table's UserID column)
- ImpersonatedUserID (int FK to Users table's UserID column)
I have a User class with properties that I want mapped to these columns:
public class User : IEntity
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmployeeNumber { get; set; }
public virtual ICollection<User> ImpersonatedUsers { get; set; }
public virtual ICollection<User> Users { get; set; }
}
In my db context class I have the following:
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UserConfiguration());
}
User configuration:
class UserConfiguration : EntityTypeConfiguration<User>
{
internal UserConfiguration()
{
this.Property(x => x.Id).HasColumnName("UserID");
this.Property(x => x.LastName).HasColumnName("Surname");
this.Property(x => x.EmployeeNumber).HasColumnName("StaffNumber");
this.HasMany(i => i.Users)
.WithMany(c => c.ImpersonatedUsers)
.Map(mc =>
{
mc.MapLeftKey("UserID");
mc.MapRightKey("ImpersonatedUserID");
mc.ToTable("Impersonations");
});
}
}
Did I link this up correctly using Entity Framework? If I pass through a user id then a list of users needs to be returned that was marked as inpersonated users. How I do this? I have this but both User and ImpersonatedUsers are empty:
return DbContext.Users
.Include("ImpersonatedUsers")
.SingleOrDefault(x => x.Id == userId);
Lets say I have the following data in my Users table (id, first name, last name, employee number):
7 Craig Matthews 123456
8 Susan Renshaw 234567
9 Michelle du Toit 34567
And the following data in my Impersonations table (impersonated id, user id, impersonated user id):
1 7 8
2 7 9
So if I pass in a user id of 7 then it need to return records for users Susan Renshaw and Michelle du Toit.
Just for clarity if you are still confused...
I have a similar situation. I have a Products table and a Specifications table. These 2 are linked up by the assication table called ProductSpecifications. I am trying to achieve the same here, but the 2 tables would be the Users table and the association table is Impersonations.

If I well understand, I think I have quite the same: a user is also a group and so can have members.
Here is an extract of my code:
public partial class User : AuthorizableBaseObject, IHasUID {
public User() {
Members = new List<User>();
}
public Guid UID { get; set; }
public DateTime EmailValidationDate { get; set; }
public String EMail { get; set; }
//public Int64 Id { get; set; }
public String Login { get; set; }
public String Password { get; set; }
public String Description { get; set; }
public DateTime LastConnectionDate { get; set; }
public Boolean CanConnect { get; set; }
public Boolean IsDisabled { get; set; }
public virtual List<User> Members { get; set; }
}
with the following configuration :
public class UserConfiguration : EntityTypeConfiguration<VDE.User> {
public UserConfiguration()
: base() {
ToTable("Users", "dbo");
Property(u => u.EMail).IsRequired().HasColumnType("nvarchar").HasMaxLength(100);
Property(u => u.Login).IsRequired().HasColumnType("nvarchar").HasMaxLength(20);
Property(u => u.Password).IsRequired().HasColumnType("nvarchar").HasMaxLength(50);
Property(u => u.Description).HasColumnType("nvarchar").HasMaxLength(200);
Property(u => u.LastConnectionDate).HasColumnType("datetime2");
HasMany(u => u.Members).WithMany().Map(m => m.MapLeftKey("UserId").MapRightKey("MemberId").ToTable("UserMembers"));
}
}
From here to get the members of a group the query is easy:
context.Users.Where(u => u.Id == someId).Select(u => u.Members).FirstOrDefault()
This will give an IEnumerable

Try taking a look at this page on inheritance. Your explanation isn't great, but I think this is what you're trying to achieve.

You must create a class for Impersonation and add it as a DBSet to the dbcontext. EF may make you put a [Key] attribute on the foreign key fields, along with [Column(Order = 0 or 1)].
Then you can lazy load the property:
public virtual List<Impersonation> UserImpersonations
{
get
{
if(_userImpersonations == null)
{
// Set _userImpersonations =
// lazy load using a linq to sql query where
// UserImpersonations.UserId = this.UserId
}
return __userImpersonations;
}
}

Related

EF Core 3.1 Owned Entities returning null objects when querying database

I have a User class with an owned entity PersonalInformation that contains properties like FirstName, LastName, PhoneNumber, etc.
The User class is a table who's Id is pulled from another login database AspNetUsers that holds all login information.
When I try and read from the database, the PersonalInformation object is always null, even though data exists in the database. Here's the classes
public class User
{
public string Id { get; set; }
public PersonalInformation PersonalInformation { get; set; }
}
public class PersonalInformation
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string MobilePhone { get; set; }
public string HomePhone { get; set; }
public string WorkPhone { get; set; }
public string OtherPhone { get; set; }
public DateTime DateOfBirth { get; set; }
}
And here's the configuration
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasKey(x => x.Id);
builder.OwnsOne(x => x.PersonalInformation, pi =>
{
pi.Property(x => x.FirstName).HasMaxLength(25);
pi.Property(x => x.MiddleName).HasMaxLength(25);
pi.Property(x => x.LastName).HasMaxLength(25);
pi.Property(x => x.MobilePhone).HasMaxLength(15);
pi.Property(x => x.HomePhone).HasMaxLength(15);
pi.Property(x => x.OtherPhone).HasMaxLength(15);
pi.Property(x => x.WorkPhone).HasMaxLength(15);
});
builder.Property(x => x.CreatedBy).HasMaxLength(36);
builder.Property(x => x.LastModifiedBy).HasMaxLength(36);
}
}
Here's an image of the database table we're pulling from
This is the call to the user table
var user = await context.Users
.FindAsync(_currentUserService.UserId);
I've also tried this one too
var user = await context.Users
.Include(x => x.PersonalInformation)
.FirstOrDefaultAsync(x => x.Id == _currentUserService.UserId);
When I examine user in the debugger, I see that User.PersonalInformation is null, even though there's data in the database.
I've looked on here for someone with a similar problem, and they suggested the following. Each did not fix my issue:
Add WithOwner() to UserConfiguration class
Add virtual to PersonalInformation on User class (as if it was a navigation property)
Add Include + FirstOrDefaultAsync() to query (as if it was a navigation property)
Upgrade to latest version of EF Core (3.1.6 as of this post)
I found the issue. The DateTime DateOfBirth was null in the database which caused the entire object to be null. Once I provided a value in the database, it worked as expected

How to create a table corresponding to enum in EF Core Code First?

How would one turn the enums used in an EF Core database context into lookup tables and add the relevant foreign keys?
Same as EF5 Code First Enums and Lookup Tables but for EF Core instead of EF 6
Related to How can I make EF Core database first use Enums?
You can use an enum in your code and have a lookup table in your db by using a combination of these two EF Core features:
Value Conversions - to convert the enum to int when reading/writing to db
Data Seeding - to add the enum values in the db, in a migration
Here below a data model example:
public class Wine
{
public int WineId { get; set; }
public string Name { get; set; }
public WineVariantId WineVariantId { get; set; }
public WineVariant WineVariant { get; set; }
}
public enum WineVariantId : int
{
Red = 0,
White = 1,
Rose = 2
}
public class WineVariant
{
public WineVariantId WineVariantId { get; set; }
public string Name { get; set; }
public List<Wine> Wines { get; set; }
}
Here the DbContext where you configure value conversions and data seeding:
public class WineContext : DbContext
{
public DbSet<Wine> Wines { get; set; }
public DbSet<WineVariant> WineVariants { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=wines.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Wine>()
.Property(e => e.WineVariantId)
.HasConversion<int>();
modelBuilder
.Entity<WineVariant>()
.Property(e => e.WineVariantId)
.HasConversion<int>();
modelBuilder
.Entity<WineVariant>().HasData(
Enum.GetValues(typeof(WineVariantId))
.Cast<WineVariantId>()
.Select(e => new WineVariant()
{
WineVariantId = e,
Name = e.ToString()
})
);
}
}
Then you can use the enum values in your code as follow:
db.Wines.Add(new Wine
{
Name = "Gutturnio",
WineVariantId = WineVariantId.Red,
});
db.Wines.Add(new Wine
{
Name = "Ortrugo",
WineVariantId = WineVariantId.White,
});
Here is what your db will contain:
I published the complete example as a gist: https://gist.github.com/paolofulgoni/825bef5cd6cd92c4f9bbf33f603af4ff
Here is another example :
public class Weather {
public int Id { get; init; }
public WeatherType Type { get; init; }
}
public enum WeatherType {
Cloudy = 1,
Sunny = 2,
Rainy = 3,
}
And you can add HasConversion in a seperate class like this :
public class WeatherEntityTypeConfiguration : IEntityTypeConfiguration<Weather>
{
public void Configure(EntityTypeBuilder<Weather> builder)
{
builder.ToTable("Weather").HasKey(k => k.Id);
builder.Property(p => p.Id).IsRequired();
builder.Property(p => p.Type).HasConversion<int>().IsRequired();
// builder.Property(p => p.Type).HasConversion<string>().IsRequired();
}
}
Note : If you use HasConversion<int>() data would be stored in database as an integer but if you use HasConversion<string>() data would be stored as string (in this example : Cloudy, Sunny or Rainy )
In addition to #PaoloFulgoni, here is how you'd do it if you want a many-to-many relationship with enums i.e. you want many user roles or wine variants 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;

How to Inherit from IdentityUserLogin And IdentityUserRole asp.net mvc

I have a two classes they are names tblUserProfile and tblUserRole I want to add multiple property to my own class that i mentioned.in fact , tblUserProfile use tblUserProfile:IdentityUserLogin and tblUserRole use tblAccessRole : IdentityUserRole but when I did add-migration it did give me this error EntityType has no key defined. Define the key for this EntityType
after that I add key attribute then EF did show me Column names in each table must be unique. Column name 'RoleId' in table 'tblAccessRoles' is specified more than once. please tell me how can i solve it.
public class tblUserProfile : IdentityUserLogin
{
public int UserID { get; set; }
public string NameFamily { get; set; }
}
public class tblAccessRole : IdentityUserRole
{
public int RoleID { get; set; }
public string Section { get; set; }
public bool IsSave { get; set; }
}
remove UserID from tblUserProfile and RoleID from tblAccessRole and
In Your DbContext add :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<tblUserProfile>()//IdentityUserLogin
.HasKey(l => new { l.LoginProvider, l.ProviderKey, l.UserId })
.ToTable("tblUserProfile");// Database Table Nam
modelBuilder.Entity<tblAccessRole>()//IdentityUserRole
.HasKey(r => new { r.UserId, r.RoleId })
.ToTable("tblAccessRole");// Database Table Name
}

Pocos - The relationship could not be changed because one or more of the foreign-key properties is non-nullable

I have three tables, and they are user, user_details and user_acls. The user table is master table, and the other two are children tables that store information related to the user. The foreign key in the latter two tables is user_id, and user_id is the primary key on the user table.
Using entity framework and pocos I have set up the following class structure, and I will only do it for one of my child tables, as once I have this resolved I should be able to apply the same logic to my other user table.
User Class
namespace myclass.Core.Domain.Customers
{
public partial class User : BaseEntity
{
private ICollection<UsersSitesAcl> _usersSitesAcls;
private ICollection<CompaniesAcl> _companiesAcls;
private ICollection<UserDetails> _userDetails;
public virtual Guid userguid { get; set; }
public virtual string first_name { get; set; }
public virtual string last_name { get; set; }
public virtual string email_address { get; set; }
public virtual System.DateTime? activated_date { get; set; }
public virtual string pwrd { get; set; }
public virtual string loginname { get; set; }
public virtual string title { get; set; }
public virtual string suffix { get; set; }
public virtual string secretquestion { get; set; }
public virtual string secretanswer { get; set; }
public virtual long? birthyear { get; set; }
public virtual string last_four_ssn { get; set; }
public virtual bool? suspended_yn { get; set; }
public virtual string account_status { get; set; }
public virtual ICollection<UsersSitesAcl> usersSitesAcls
{
get
{
var sitesAcls = new List<UsersSitesAcl>();
if (_usersSitesAcls != null)
{
var query = from usa in _usersSitesAcls
where usa.active_yn
select usa;
sitesAcls = query.ToList();
}
return sitesAcls;
}
protected set { _usersSitesAcls = value; }
}
public virtual ICollection<CompaniesAcl> companiesAcls
{
get
{
var companyAcls = new List<CompaniesAcl>();
if (_companiesAcls != null)
{
var query = from ca in _companiesAcls
where ca.active_yn
select ca;
companyAcls = query.ToList();
}
return companyAcls;
}
protected set { _companiesAcls = value; }
}
public virtual ICollection<UserDetails> userDetails
{
get
{
var userDetails = new List<UserDetails>();
if (_userDetails != null)
{
userDetails = (from ud in _userDetails where ud.active_yn select ud).ToList();
}
return userDetails;
}
set { _userDetails = value; }
}
}
}
User Details Class
namespace myclass.Core.Domain.Customers
{
public partial class UserDetails : BaseEntity
{
private User _updatedByUser;
public virtual long user_id { get; set; }
public virtual string primary_specialty { get; set; }
public virtual string secondary_specialty { get; set; }
public virtual string npi { get; set; }
public virtual string state_licence_number { get; set; }
public virtual string issuing_state { get; set; }
public virtual string dea_number { get; set; }
public virtual string dea_schedule1 { get; set; }
public virtual string dea_schedule2 { get; set; }
public virtual string dea_schedule3 { get; set; }
public virtual string dea_schedule4 { get; set; }
public virtual string dea_schedule5 { get; set; }
public virtual string dea_expire_date { get; set; }
public virtual string state_licence_expire_date { get; set; }
public virtual string provider_rights { get; set; }
public virtual long updated_by_user_id { get; set; }
public virtual User updatedByUser
{
get { return _updatedByUser; }
protected set
{
_updatedByUser = value;
updated_by_user_id = _updatedByUser.Id;
}
}
}
}
For my mapping I have the following structure for both the user and user details
UserMap
namespace myclass.Data.Mappings.Domains.Customers
{
public partial class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
this.ToTable("users");
this.HasKey(u => u.Id);
this.Property(u => u.Id).HasColumnName("user_id");
this.Property(u => u.userguid).IsRequired();
this.Property(u => u.first_name).HasMaxLength(50).IsRequired();
this.Property(u => u.last_name).HasMaxLength(50).IsRequired();
this.Property(u => u.pwrd).HasMaxLength(100).IsRequired();
this.Property(u => u.email_address).HasMaxLength(100).IsRequired();
this.Property(u => u.loginname).HasMaxLength(50).IsRequired();
this.Property(u => u.activated_date).IsOptional();
this.Property(u => u.create_date).IsRequired();
this.Property(u => u.active_yn).IsRequired();
this.Property(u => u.title).IsOptional();
this.Property(u => u.suffix).IsOptional();
this.Property(u => u.last_four_ssn).IsOptional();
this.Property(u => u.secretquestion).IsOptional();
this.Property(u => u.secretanswer).IsOptional();
this.Property(u => u.birthyear).IsOptional();
this.Property(u => u.account_status).IsOptional();
this.Property(u => u.suspended_yn).IsOptional();
this.HasMany(u => u.userDetails).WithRequired().HasForeignKey(ud => ud.user_id);
this.HasMany(u => u.usersSitesAcls).WithRequired().HasForeignKey(usa => usa.user_id);
this.HasMany(u => u.companiesAcls).WithRequired().HasForeignKey(ca => ca.user_id);
}
}
}
UserDetails Map
enter code here
namespace myclass.Data.Mappings.Domains.Customers
{
public partial class UserDetailsMap:EntityTypeConfiguration<UserDetails>
{
public UserDetailsMap()
{
this.ToTable("user_details");
this.HasKey(ud => ud.Id);
this.Property(ud => ud.Id).HasColumnName("user_detail_id");
this.Property(ud => ud.user_id).IsRequired();
this.Property(ud => ud.primary_specialty).HasColumnName("primary_speciality").IsOptional();
this.Property(ud => ud.secondary_specialty).IsOptional();
this.Property(ud => ud.npi).IsOptional();
this.Property(ud => ud.state_licence_number).HasColumnName("StateLicenseNumber").IsOptional();
this.Property(ud => ud.issuing_state).HasColumnName("IssuingState").IsOptional();
this.Property(ud => ud.dea_number).HasColumnName("DEANumber").IsOptional();
this.Property(ud => ud.dea_schedule1).HasColumnName("DEASchedule1").IsOptional();
this.Property(ud => ud.dea_schedule2).HasColumnName("DEASchedule2").IsOptional();
this.Property(ud => ud.dea_schedule3).HasColumnName("DEASchedule3").IsOptional();
this.Property(ud => ud.dea_schedule4).HasColumnName("DEASchedule4").IsOptional();
this.Property(ud => ud.dea_schedule5).HasColumnName("DEASchedule5").IsOptional();
this.Property(ud => ud.dea_expire_date).HasColumnName("DeaExpireDate").IsOptional();
this.Property(ud => ud.state_licence_expire_date).HasColumnName("StateLicenseExpireDate").IsOptional();
this.Property(ud => ud.provider_rights).HasColumnName("ProviderRights").IsOptional();
this.Property(ud => ud.active_yn).IsRequired();
this.Property(ud => ud.create_date).IsRequired();
this.Property(ud => ud.updated_by_user_id).IsRequired();
this.HasRequired(ud => ud.updatedByUser).WithMany().HasForeignKey(ud => ud.updated_by_user_id);
//
}
}
}
First of I am adding a new record, and I set the contents of the User class (bar the User_details and user_acls, and it goes out and creates the record for the user without any issues.
However, when I try to do the same for the user_details and user_acls, I have not had any success, and have tried to adding the user_details as item to the property on the user table without any success.
And have even tried saving it out as a separate record, where I store the user record, retrieve the user_id of the new record added. Then create a new object for user_details and user_acls, and try to a save on that and I end up with the following message:
What do I need to do to make this work, I have tried everything I know and no success. So any help ye can give would be appreciated. Thanks.
Blockquote
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
That's a lot of code to read but I think the issue is that you're modifying the entity state while it's not being tracked by the entity framework.
Try something like
db.Entry(currentyUser).State = EntityState.Modified;
before saving changes.
You show a lot of code but not, I suspect, the code in question.i.e. how you create a user and attach user detail and then how you save those objects.
A simple test can be to use
this.Ignore(ud=>ud.User)
as part of your user detail mapping.
If this works you can be fairly certain the issue is that the User associated with the UserDetail has a state other than Unchanged. What should be happening is that the User should be getting saved first and then having its state set to Unchanged and then the User Details should be getting saved.
Because you dont show your attach and save code I can only speculate but I would bet money that the state of User is not Unchanged, probably Added or some custom state like Inactive if you are using disconnected entities.
Therefore EF tries to recreate the User object ( if state == Added) and then reassign it to the UserDetails which somehow orphans the User Details by nulling the previous User object association and then attaching the new.
I'm still working through the docs on exactly how this happens but my own experience just this last half hour has been that the parent / foreign key object had a state other than Unchanged. So EF is differentiating the reference of User in your User Details object from what it has in it's DBSets. Thus the null reference.
I understand you are saving a new User and their details at the same time but look carefully at your code as to how you do that. Most likely there is some kind of "User swap" going on whereby the User you save first is not actually the User that's attached to UserDetails when trying to save them. Are you doing any Linq ToList() type "stuff" and inadvertently switching references to an non Unchanged User object.
In my case it was because I was creating a "stub" object in code of an FK object in the db and I overlooked settings it's state to Unchanged before associating it with my detail object.
Sorry that my answer is a bit of a waffle but Im just working it through it now. When I understand it more consisely I will update this answer.
I figured this out and it had to do with the way I was accessing the user object using autofac, in the end it was something on my end.

How to map a related table with no primary key with fluent-NHibernate

Looks a common situation to me: I have two tables:
documents:
dID (pk, int), dName(varchar)
and document_options:
dID (int), oType(int), oValue(varchar)
I would like to have a class Document with a property Options (a List of DocumentOption class)
Since document_options has no PK I cannot use HasMany, and rows from this table don't seem like 'real' entities anyway...
I see a way to generate an auto-number key for document options and map with HasMany, or maybe create a composite ID, but I'd like to know if there is a better option that I don't know about.
In this case, DocumentOptions is a value object, since it has no identity of its own and has no meaning outside of the document it belongs to. So, you would use Component to map the collection properties to the value object.
public class Document : Entity // don't worry about Entity; it's a base type I created that contains the Id property
{
public virtual string Name { get; set; }
public virtual IList<DocumentOptions> Options { get; protected set; }
public Document()
{
Options = new List<DocumentOptions>();
}
}
public class DocumentOptions
{
public virtual int Type { get; set; }
public virtual string Value { get; set; }
}
And the mapping:
public DocumentMap()
{
Table("documents");
Id(c => c.Id)
.Column("dId")
.GeneratedBy.HiLo("10");
Map(c => c.Name)
.Column("dName");
HasMany(c => c.Options)
.Component(c =>
{
c.Map(c2 => c2.Value).Column("oValue");
c.Map(c2 => c2.Type).Column("oType");
})
.Table("document_options")
.KeyColumn("dId")
.Cascade.AllDeleteOrphan();
}
If I understand correctly I had to map options as a list of components:
HasMany(x => x.DocumentOptions)
.Table("document_options")
.KeyColumn("dID")
.Component(c => {
c.Map(x => x.Option, "oID");
c.Map(x => x.Value, "oValue");
})
.Fetch.Subselect(); //This type of join isn't strictly needed, is used for SQL optimization
classes FYI:
public class Options {
public virtual int Option { get; set; }
public virtual int Value { get; set; }
}
public class Document {
public virtual int ID { get; set; }
public virtual String Name { get; set; }
public virtual IList<DocumentOption> DocumentOptions { get; set; }
}

Categories

Resources