IdentityDbContext User DbSet Name - c#

I've created a custom user inheriting from IdentityUser called Contacts, and my applications dbcontext inherits from IdentityDbContext like so:
public class Contact : IdentityUser<int, ContactLogin, ContactRole, ContactClaim>
{
public Contact()
{
}
}
public class dbcontext : IdentityDbContext<Contact, Role, int, ContactLogin, ContactRole, ContactClaim>
{
public dbcontext()
: base("dbcontext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// IdentityDbContext base - must be called prior to changing identity configuration
base.OnModelCreating(modelBuilder);
// custom identity table names and primary key column Id names
modelBuilder.Entity<Contact>().ToTable("Contacts").Property(p => p.Id).HasColumnName("ContactId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<ContactRole>().ToTable("ContactRoles");
modelBuilder.Entity<ContactLogin>().ToTable("ContactLogins");
modelBuilder.Entity<ContactClaim>().ToTable("ContactClaims").Property(p => p.Id).HasColumnName("ContactClaimId");
modelBuilder.Entity<Role>().ToTable("Roles").Property(p => p.Id).HasColumnName("RoleId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
By default IdentityDbContext contains a Users DbSet. Is it possible to change the name of this DbSet to match the type that it's implementing, e.g Contacts?
It's not a big deal, but it would just be nice to refer to the DbSet using dbcontext.Contacts instead of dbcontext.Users.
Thanks.

The base IdentityDbContext uses: public virtual IDbSet<TUser> Users { get; set; } to expose the Users DbSet.
You'll need a similar property for your own implementation, e.g: public IDbSet<Contacts> Contacts { get; set; }
Update
Question was regarding renaming the existing DbSet of Contacts from Users to Contacts.
No, you can't do this out of the box. You could attempt to wrap it and expose it again, but this isn't really the right thing to do. See this question for an in depth discussion.
Just a note that if you decide to overwrite anything or add your own, the default EF implementation of UserStore will use the DbSet named Users. Just something to keep an eye on if you get unexpected behavior.

Generally what I tend to do is have a big separation of concerns right.
So I have:
public IDbSet<User> Users { get; set; }
This represents anyone who wants to log into my system. So now I want to model actual concepts into my database, concepts that relate to real world things. So I have a system administrator for example, I will create an entity for this.
public class SystemAdministrator
{
public string Name { get; set; }
public int LocationId { get; set; } // a complex representation of where this administrator works from
public int UserId { get; set; } // this is now a reference to their log in
}
Now my context will look like this:
public IDbSet<User> Users { get; set; }
public DbSet<SystemAdministrator> SystemAdministrators { get; set; } // I use DbSet because it exposes more methods to use like AddRange.
This means now my database has proper representations of real world concepts which is easy for everyone to develop against. I do the same for Clients or Employees.
This also means that I can move away from primitive obsession

Related

User details within the DbContext model builder

I'm new to razor pages / efcore / aspnet identity and have been trying to figure this out but its beating me.
Basically, I use AspNet Identity for user authentication & authorisation. I've extended AspNetUsers with an additional OrganisationId, which is an FK to Organisation entity; and added the ID as a claim in the identity claim store. This works fine.
Now I need to set an efcore global filter based on the authenticated user's organisationId so they can only view data that is assigned to their organisation.
However, I can't access the authenticated user details within the ModelBuilder.
public class SDMOxContext : IdentityDbContext<
ApplicationUser, ApplicationRole, string,
ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin,
ApplicationRoleClaim, ApplicationUserToken>
{
public SDMOxContext(DbContextOptions<SDMOxContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Set global filter so users can only see projects within their organisation.
builder.Entity<Project>().HasQueryFilter(project => project.OrganisationId == 1);
}
Instead of 1 in the global filter, I need to enter the user organisationid, which is stored as a user claim. Usually I get it with this:
User.FindFirstValue("OrganisationId")
However, User doesn't exist in the current context.
So I would need to apply the query filter at a later stage, ie. after user authentication? Any pointers where to start with a mid-tier/logic-tier approach?
Granted this is an opinion on architecture, but I break it down like this:
Data-Tier - This tier's responsibility to to access resources (normally) outside the executing application. This includes; Databases, File IO, Web Api's etc.
Business/Logic-Tier - This tier's responsibility (which could be broken down further) should Authenticate, Authorize, Validate and build objects that represent the businesses needs. To build these objects, it may consume one or more data access objects (for example, it may use an IO DA to retrieve the Image from a local file system or Azure storage and a Database DA to retrieve metadata about that image).
Presentation/Exposure-Tier - This tier's responsibility is to wrap and transform the object into the consumers need (winforms, wpf, html, json, xml, binary serialization etc).
By leaving logic out of the data-tier (even in multi-tenant systems) you gain the ability to access data across all systems (and trust me there is a lot of money to be made here).
This is probably way more than I can explain in such a short place and very my opinion. I'm going to be leaving out quite a bit but here goes.
Data-Tier
namespace ProjectsData
{
public interface IProjectDA
{
IProjectDO GetProject(Guid projectId, Guid organizationId);
}
private class ProjectDA : DbContext, IProjectDA
{
public ProjectDA (...)
public IEnumerable<ProjectDO> Projects { get; set; }
protected override void OnModelCreating(ModelBuilder builder) {... }
public IProjectDO GetProject(Guid projectId, Guid organizationId)
{
var result = Projects
.FirstOrDefault(p => p.Id == projectId && OrganizationId = organizationId);
return result;
}
}
public interface IProjectDO{ ... }
private class ProjectDO: IProjectDO
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
public Guid CategoryId { get; set; }
}
}
Logic
namespace ProjectBusiness
{
public interface IProjectBO { .. }
public interface IOrganization
{
Guid OrganizationId { get; }
}
private class ProjectBA : IProjectBO
{
private readonly IProjectDA _projectDA;
private readonly IIdentity _identity;
private readonly IOrganization _organization;
public ProjectLogic(IProjectDA projectDA,
IIdentity identity,
IOrganizationContext organizationContext)
{
_projectDA = projectDA;
_identity = identity;
}
public IProjectBO GetProject(Guid id)
{
var do = _projectDA
.GetProject(id, _organization);
var result = map.To<ProjectBO>(do);
return result;
}
}
public interface IProjectBO { .. }
private class ProjectBO
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
public Guid CategoryId { get; set; }
}
}
So under these circumstances the data layer is aware of type of request, but isn't multi-tenant aware. It isn't limiting all request based on anything. This architecture is advantageous in a number of ways.
First, in the above example, your product takes off and your supervisor wants to know what Categories are the most popular.
namespace StatisticsBusiness
{
public interface IStatisticsBO
{
IEnumerable<ICategoryStatisticBO> CategoryStatistics { get; set; }
}
public interface ICategoryStaticBO
{
Guid CategoryId { get; }
int ProjectCount { get; }
}
private class StatisticsBA : IStatisticsBO
{
private readonly IProjectDA _projectDA;
private readonly IIdentity _identity;
public ProjectLogic(IProjectDA projectDA,
IIdentity identity)
{
_projectDA = projectDA;
_identity = identity;
}
public IEnumerable<IProjectBO GetOrderedCategoryPopularity()
{
var dos = _projectDA
.GetProjectCategoryCounts()
var result = map.To<IEnumerable<IStatisticsBO>>(dos);
return result;
}
}
public interface IStatisticsBO{ .. }
private class StatisticsBO
{
public Guid CategoryId { get; }
public int ProjectCount { get; }
}
}
Note: Some people prefer to pass an expression as a predicate. Both have their advantages and disadvantages. If you decide to go the predicate route, then you'll have to decide if all your Data Access types use predicates or not. Just realize that using predicates against IO or Web Api might be more effort that it's worth.
Secondly, some requirement causes you not to be able to use Entity Framework. You replace it with Dapper or some other new better technology/framework. All you have to create new I<whataver>DA classes because the consuming logic is unaware of anything other than those interfaces (programming against an interface, the L in SOLID programming principles and the I in SOLID programming principles).
I don't use this pattern all the time because for some smaller websites, it's too much work for the payoff.
I will suggest to decompose the solution in tow parts
Add an organization id in your dbcontext, much like a tenant id in multi-tenant env. See this link for example.
Next challenge will be to pass the organization id as a parameter to DbContext constructor. For this you can create a factory for DbContext. Since you store the OrganizationId in claims. The factory can access the same claim HttpContext and pass the organization id as a parameter while instanting the dbContext.
It's not perfect but can give you a starting point.

How to require many to many relation with code first

I'm using Entity Framework with Code First to create a database and so on.
public class Company : Entity // Entity contains an ID and a RowVersion
{
// some Properties
//
public virtual ICollection<Profile> Profiles { get; set; }
}
// No Attributes here, because this Model has to be reusable
public class Profile : Entity
{
// some Properties
//
public virtual ICollection<Company> Companies { get; set; }
}
I am trying to require a Company to have a Profile (cn:n), that manages that Company. I've got some "Business Logic" that takes care of editing the entities, mainly because of the validation part. That Logic would validate if the required relations are set anyways. I've got two Questions here:
Should the DB have the relations optional (for editing conveniance)? The "Business Logic" would have to be the only one editing the DB.
Or else how would i set the relation required?
I wouldn't mind if you go a bit of course, there is far too few about many to many relations with EF on the internet, in my opinion. (I'm new here, so never mind if this goes against some rule)
EDIT
I'm aware of the way with the 3rd table, but that is my point: How to require many to many relation with code first
When designing a database when this situation arises (many to many) use an additional table to separate this regard. For example, we have two tables t1 and t2 with t1 many to many to t2, create a table t3 that is relevant:
t1 one-to-many to t3 and t2 one-to-many t3.
In your situation, you need to create an additional class, like this:
public class CompanyProfile {
public virtual ICollection<Company> Companies { get; set; }
public virtual ICollection<Profile> Profiles { get; set; }
}
In your classees Company and Profile create the link to CompanyProfile class.
You need to turn your entity into a validatable object by implementing the IValidatableObject interface, with a method that will check that you always have at least 1 item in the collection. The method is called by EF when you call SaveChanges()
public class Company : Entity, IValidatableObject
{
public virtual ICollection<Profile> Profiles { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!Profiles.Any())
yield return new ValidationResult("Company must have at least 1 Profile");
}
}
public class Profile : Entity, IValidatableObject
{
public virtual ICollection<Company> Companies { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!Companies.Any())
yield return new ValidationResult("Profile must have at least 1 Company");
}
}
Reference: IValidateableObject example

Fluent mapping inheritance

I have an issue I don't know the best solution for. Hopefully someone here can help =)
What I'm trying to solve:
We have to type of users in a system, person and organization.
I want to have a shared login table for the two (ie the user probably won't know which type of user they are, they just relate to username and password).
So I've created a login table for usernames and passwords. But I need to know who the login is connected to, so I need a reference to either person or organization.
Consider the following classes (simplified):
public class Login
{
public string Username {get; set;}
public string Password {get;set;}
}
public class LoginPerson : Login
{
public Person Person {get;set;}
}
public class LoginOrg : Login
{
public Organization Organization {get;set;}
}
public class Person
{
public LoginPerson LoginPerson {get;set;}
//Lots of other properties. Removed for simplicity
}
public class Organization
{
public LoginOrg LoginOrg {get;set;}
//Lots of other properties. Removed for simplicity
}
The person configuration is set up as follows:
public class PersonConfiguration : EntityTypeConfiguration<Person>
{
public PersonConfiguration()
{
HasRequired(p => p.LoginPerson).WithRequiredPrincipal(p => p.Person);
}
}
First of all, this doesn't work. I get an exception saying
"System.Data.EntityCommandCompilationException: System.Data.EntityCommandCompilationException: An error occurred while preparing the command definition. See the inner exception for details. ---> System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.."
So my first question is why doesn't this work?
My second question is: which strategy is best suited for this kind of inheritance? TPT, TPH or TPC?
Well, for starters, none of your entities have keys. You need a primary key to make them work. EF uses a convention to do this, which is the class name plus Id at the end, like PersonId, or you key be explicit with an attribute of [Key]
Second, your model is confusing and fairly circular. And without primary keys, there's no way to to create associations.
I'm confused about why you have a member that is a LoginPerson in a Person object, and the same for an Organization? In any event, you really need to rethink this model and figure out what your keys are.
The solution to my exception was to set up the correct configuration ;-)
PersonConfiguration didn't need to include any configuration for the LoginPerson property. I added a LoginPersonConfiguration ->
public class LoginPersonConfiguration : EntityTypeConfiguration<LoginPerson>
{
public LoginPersonConfiguration()
{
ToTable("LoginPerson");
HasKey(l => l.Id);
HasRequired(l => l.Person).WithOptional(p => p.LoginPerson).Map(t => t.MapKey("PersonId"));
}
}
And I also had to add the Login to the DbContext class
public class MyDbContext : DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<Login> Logins { get; set; }
}
When it comes to which strategy is best, I have decided to go for TPT.

Entity Framework - entities with inherited properties [duplicate]

This question already has an answer here:
How to map inherited entities in EF code-first
(1 answer)
Closed 2 years ago.
I searched the threads on here and found multiple similar posts but no solutions
Assume I have a User table in my db that I've mapped to a simple User entity
public class User{
public int UserId {get;set;}
public string Username {get;set;}
}
I want to create a new class that will encapsulate an ExternalUser which has all the same fields as User but adds a few more fields. The fields for my ExternalUser will be populated from a view in the db that pulls in both the fields from User and the additional fields required for ExternalUser
public class ExternalUser : User{
public int SomeExternalId{get;set;};
public string SomeExternalProp{get;set;};
}
but no matter how I seem to define my mappings for this new object I get the following error:
The property 'UserId' is not a declared property on type 'ExternalUser'. Verify that the property has not been explicitly excluded from the model by using the Ignore method or NotMappedAttribute data annotation. Make sure that it is a valid primitive property.
Can someone share the correct way to map this. Its stuff like this that makes me hate EF, simply inheriting a POCO shouldn't cause it to blow up, especially not when all the fields exist in the underlying view that I'm pointing to. Much thanks!
I am not sure about your use case but if you have the option to make User an abstract base class, then you can use the Table per Concrete Type approach.
You would need to make User an abstract class and call MapInheritedProperties() when creating the model mappings for ExternalUser:
public abstract class User
{
public int UserId { get; set; }
public string Username { get; set; }
}
[Table("ExternalUser")]
public class ExternalUser : User
{
public int SomeExternalId { get; set; }
public string SomeExternalProp { get; set; }
}
Note that I am using [Table] attribute to map the entity name to table name (you can also do this in OnModelCreating method but I find it cleaner to use the [Table] attribute):
And this is OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
Database.SetInitializer<ApplicationDbContext>(new CreateDatabaseIfNotExists<ApplicationDbContext>());
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
// I believe the inherited properties are mapped by default
modelBuilder.Entity<ExternalUser>().Map(m =>
{
m.MapInheritedProperties();
});
}
Note that I am removing PluralizingTableNameConvention as I don't want plural table names.

NHibernate, simple question, extra association

I've got this question that's been bugging me and my vast and unfathomable intellect just can't grasp it. The case: I want to make multiple one-to-many relationships to the same entity using the fluent nhibernate automapper and export schema.
I have:
Base Class:
public abstract class Post<T> : Entity, IVotable, IHierarchy<T>
{
public virtual string Name
{
get; set;
}
public virtual string BodyText
{
get; set;
}
public virtual Member Author
{
get; set;
}
}
and Inheriting Class:
[Serializable]
public class WallPost : Post<WallPost>
{
public virtual Member Receiver
{
get; set;
}
}
The 'Member' properties of WallPost is a foreign key relationship to this class:
public class Member : Entity
{
public Member()
{
WallPosts = new IList<WallPost>();
}
public virtual IList<WallPost> WallPosts
{
get; set;
}
}
I hope you're with me until now. When I run the exportschema of nhibernate I expect to get a table wallpost with 'author_id' and 'receiver_id' BUT I get author_id, receiver_id,member_id. Why did the Nhibernate framework add member_id, if it's for the collection of posts (IList) then how do you specify that the foregin key relationship it should use to populate is receiver, i.e. member.WallPosts will return all the wallposts of the receiver.
I hope i made sense, if you need anything else to answer the question let me know and I'll try to provide.
Thanks in advance
P.s. If I change the property name from 'Receiver' to 'Member' i.e. public virtual Member Member, only two associations are made instead of 3, author_id and member_id.
E
THE SOLUTION TO THIS QUESTION FOR ANYONE ELSE WONDERING IS TO ADD AN OVERRIDE:
mapping.HasMany(x => x.WallPosts).KeyColumn("Receiver_id");
Most likely (I don't use auto mapping, I perfer to write my own classmaps) it's because the auto mapping assuming that your Member's "WallPosts" is controled by the wall posts. So it creates a member_id for that relationship.
Edit: Try adding an override in your fluent config, after AutoMap add:
.Override<Member>(map =>
{
map.HasMany(x => x.WallPosts, "receiver_id")
.Inverse;
});

Categories