How to include related entities when getting ApplicationUser from DB? [duplicate] - asp.net-mvc

I've extended IdentityUser to include a navigation property for the user's address, however when getting the user with UserManager.FindByEmailAsync, the navigation property isn't populated. Does ASP.NET Identity Core have some way to populate navigation properties like Entity Framework's Include(), or do I have to do it manually?
I've set up the navigation property like this:
public class MyUser : IdentityUser
{
public int? AddressId { get; set; }
[ForeignKey(nameof(AddressId))]
public virtual Address Address { get; set; }
}
public class Address
{
[Key]
public int Id { get; set; }
public string Street { get; set; }
public string Town { get; set; }
public string Country { get; set; }
}

Unfortunately, you have to either do it manually or create your own IUserStore<IdentityUser> where you load related data in the FindByEmailAsync method:
public class MyStore : IUserStore<IdentityUser>, // the rest of the interfaces
{
// ... implement the dozens of methods
public async Task<IdentityUser> FindByEmailAsync(string normalizedEmail, CancellationToken token)
{
return await context.Users
.Include(x => x.Address)
.SingleAsync(x => x.Email == normalizedEmail);
}
}
Of course, implementing the entire store just for this isn't the best option.
You can also query the store directly, though:
UserManager<IdentityUser> userManager; // DI injected
var user = await userManager.Users
.Include(x => x.Address)
.SingleAsync(x => x.NormalizedEmail == email);

The short answer: you can't. However, there's options:
Explicitly load the relation later:
await context.Entry(user).Reference(x => x.Address).LoadAsync();
This will require issuing an additional query of course, but you can continue to pull the user via UserManager.
Just use the context. You don't have to use UserManager. It just makes some things a little simpler. You can always fallback to querying directly via the context:
var user = context.Users.Include(x => x.Address).SingleOrDefaultAsync(x=> x.Id == User.Identity.GetUserId());
FWIW, you don't need virtual on your navigation property. That's for lazy-loading, which EF Core currently does not support. (Though, EF Core 2.1, currently in preview, will actually support lazy-loading.) Regardless, lazy-loading is a bad idea more often than not, so you should still stick to either eagerly or explicitly loading your relationships.

Update for .NET 6.0 with EF Core 6.0:
You can now configure the property to be automatically included on every query.
modelBuilder.Entity<MyUser>().Navigation(e => e.Address).AutoInclude();
For more info check out:
https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager#model-configuration-for-auto-including-navigations

I found it useful to write an extension on the UserManager class.
public static async Task<MyUser> FindByUserAsync(
this UserManager<MyUser> input,
ClaimsPrincipal user )
{
return await input.Users
.Include(x => x.InverseNavigationTable)
.SingleOrDefaultAsync(x => x.NormalizedUserName == user.Identity.Name.ToUpper());
}

Best Option in my case is to add a package reference to Microsoft.EntityFrameworkCore.Proxies and then in your services use the UseLazyLoadingProxies
.AddDbContext<YourDbContext>(
b => b.UseLazyLoadingProxies()
.UseSqlServer(myConnectionString));
More infos
https://learn.microsoft.com/de-de/ef/core/querying/related-data/lazy

Related

Best way to check if record exists and use site wide

I am fairly new to c# and MVC but I am building an intranet app. Being on the internal network there is no need to sign in to use the app but I do have it connected to a database which has an 'Administration' table. In this table are the administrator's email addresses. I am also using System.DirectoryServices.AccountManagement and then UserPrincipal.Current.EmailAddress to get the users email address. What I would like to do is compare the UserPrincipal.Current.EmailAddress to the database table and if there is a match then set a boolean to TRUE that I can reference/call upon within my entire site.
I have a model matching the database tables and I can also query the database using a where statement to the value of UserPrincipal.Current.EmailAddress but only within a set method (ActionResult) and return the boolean value within a viewbag to that particular controller that is accessed by the related view only.
I would like to know what is best practice for setting up my site so that whichever page a users visits their email is compaired to the database and a boolean is set to true/false if they are/aren't in the database administrator table.
Edit: Would this be to create a base controller and then inherit it in all other controllers and within the base controller perform the database query - if so a little guidance would be greatly appricated
My current set up is an EmailEntityModel:
using System;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
public partial class EmailEntities : DbContext
{
public EmailEntities()
: base("name=EmailEntities")
{
}
public virtual DbSet<Audience> Audiences { get; set; }
public virtual DbSet<CallToAction> CallToActions { get; set; }
public virtual DbSet<ColourScheme> ColourSchemes { get; set; }
public virtual DbSet<Email> Emails { get; set; }
public virtual DbSet<EmailType> EmailTypes { get; set; }
public virtual DbSet<Administrator> Administrators { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
Then I have an email Controller:
public class EmailsController : Controller
{
private EmailEntities db = new EmailEntities();
public ActionResult Index()
{
return View(db.Emails.ToList());
}
Can I use the EmailEntities to query the Administator DBset within my controller but can I use this elsewhere?
If I understood your question correctly, you want to query the DB on every request and compare the current user's email against the admin email. If that's the case, then you have many options.
If it was me, I would keep the Admin email in a constant/static variable (so I don't have to make the trip to the DB on every request):
public static class StaticCache
{
// static constructor would run only once, the first it is used
// this value is maintained for the entire life-time of the application
static StaticCache()
{
using (var context = MyApplicationDbContext.Create())
{
// get your admin email for the DB
AdminEmail = context.Email.Where(/*some admin flag == true*/).FirstOrDefault();
}
}
public static string AdminEmail;
public static bool IsAdminUser(string curEmail)
{
return string.Equal(curEmail, AdminEmail, StringComparison.InvariantCultureIgnoreCase);
}
}
That's it. Now you can call StaticCache.IsAminUser() anywhere in your program (even in your view). All you need is to pass the current email to the method.

Multiple database contexts when using repository pattern

I am a bit lost right now... I've never seen this much divergent information regarding solution to the problem. But let us start from the beginning.
I am using ASP.NET MVC with Repositories injected to Controllers, thanks to the Ninject. I have 2 simple Entities: Admin with a list of created blog entries and Entries with one virtual Admin field.
Admin:
public class Admin
{
[Key, ScaffoldColumn(false)]
public int Id { get; set; }
[Required(ErrorMessage = "Zły login.")]
[StringLength(20), MinLength(3)]
[RegularExpression(#"^[a-zA-Z0-9]*$", ErrorMessage = "Special characters are not allowed.")]
public string Login { get; set; }
[Required(ErrorMessage = "Złe hasło.")]
[StringLength(20, MinimumLength = 3)]
[DataType(DataType.Password)]
[Display(Name = "Hasło")]
public string Password { get; set; }
public virtual List<Entry> CreatedEntries { get; set; } // napisane aktualności przez danego admina
}
Entry:
public class Entry
{
[Key, ScaffoldColumn(false)]
public int Id { get; set; }
[StringLength(200, MinimumLength = 2)]
[DataType(DataType.Text)]
[Display(Name = "Tytuł")]
public string Title { get; set; }
[Required, StringLength(2000), MinLength(3)]
[Display(Name = "Treść")]
[UIHint("tinymce_jquery_full"), AllowHtml]
public string Text { get; set; }
public virtual Admin Admin { get; set; }
}
You probably know where it is going, since this problem is... "classic" on stackoverflow.
In the Controller I want to bind one object to another:
entry.Admin = repAdmins.GetAdmin(User.Identity.Name);
repEntries.AddEntry(entry);
In the repository:
public void AddEntry(Entry entry)
{
db.Entries.Add(entry);
db.SaveChanges();
}
Of course I can't do that, because of famous "An entity object cannot be referenced by multiple instances of IEntityChangeTracker", which is a result of having separate database contexts in each repository.
When I was searching for a solution I already knew that probably the best way to solve it is to use one common context. And then I discovered Unit Of Work pattern. But here's when the real problems starts.
On many sites the solution to this is a bit different.
The repositories must have common generic interface (which I don't want to use, because I don't need to have each CRUD operation on each Entity, plus sometimes I need to have extra methods like "IfExists", etc.)
On few sites I've read that this whole abstraction is not needed, since abstraction is already provided with Entity Framework and UoW is implemented in DbContext (whatever that means)
The Unit Of Work pattern (at least from examples on the internet) seems to be a real pain for me...
I need some guidance... I learn ASP.NET MVC for only a year. For me it seems like it's a "triumph of form over content". Because... What I simply need is to bind one object to another. I'm starting to think that it was better when I simply had a context object in the Controller and I didn't need to build Eiffel Tower to achieve what's mentioned above :\ However I like idea of repositories...
I'll open by simply answering the question straight-out. Simply, your repository should take the context as a dependency (it should have a constructor that accepts a param of type DbContext). Your context should be managed by Ninject, and then injected into your repository and/or your controller. That way, everything always uses the same context. You should do all this in "request" scope, so that the context is specific to the current request.
That said, I'd like to hit some of your other points. First, a repository is just a method of access. It really shouldn't be dependent on the entity. It's okay to have methods that you don't intend to use on a particular entity: just don't use them. However, if you do want to enforce this, you can always use generic constraints and interfaces. For example, let's say you don't want update available on a particular entity. You could have interfaces like:
public interface ICreateable
{
}
public interface IUpdateable : ICreateable
{
}
Then, your entity that should not be updated will implement only ICreateable while other entities (which allow update) would implement IUpdateable (which by interface inheritance, also implement ICreateable). Finally, you would add constraints on your repository methods:
public void Create<TEntity>(TEntity entity)
where TEntity : class, ICreateable
public void Update<TEntity>(TEntity entity>)
where TEntity : class, IUpdateable
Since, the entity in question only implements ICreatable, it will not be eligible to be used as a type param to Update, so there's then no way to utilize that method.
Next, the advice to not use the repository/UoW patterns with Entity Framework is indeed because Entity Framework already implements these patterns. The repository pattern exists as a way to contain all the database querying logic (constructing SQL statements and such) in one place. That is the "abstraction" we're talking about here. In other words, instead of directly constructing SQL statements in your application code, that code is abstracted away into a repository. However, this is exactly what Entity Framework does, which is why you don't need to do it again. The Unit of Work pattern exists as a method to orchestrate the work of multiple repositories, allowing things like transactions. However, again, Entity Framework does all this.
The only reason to add any further abstraction is if you want to abstract the actual provider, i.e. Entity Framework itself. For example, you could have an interface like IRepository and then create implementations like EntityFrameworkRepository, NHibernateRepository, WebApiRepository, etc. Your application would only ever depend on IRepository, and you could then sub in different implementations as needed. If you're not going to do this, or you will always be using Entity Framework, then you might as well just use your context directly. Any further abstraction is just something else to maintain with no benefit at all to your application.
Finally, yes, the Unit of Work pattern is a real pain to everyone, not just you. Which is why I forgo it entirely. I use what I call a "truly generic repository", which utilizes generic methods and interfaces to handle any entity I want to throw at it. That means it acts not only as a repository but also a unit of work as well. You only need one instance per context and it's provider-agnostic. For more information check out the article I wrote on the subject over on my website.
The following example shows how to use the same context within multiple repositories. To simplify it, I did not use interfaces and nor did I use a container to inject dependencies.
Controller class:
public class HomeController : Controller
{
Context context;
AdminRepository adminRepository;
EntryRepository entryRepository;
public HomeController()
{
context = new Context();
adminRepository = new AdminRepository(context);
entryRepository = new EntryRepository(context);
}
// GET: Home
public ActionResult Index()
{
string login = "MyLogin";
Admin admin = adminRepository.GetAdmin(login);
Entry entry = new Entry() { Admin = admin};
entryRepository.AddEntry(entry);
return View(entry);
}
}
Repositories:
public class AdminRepository
{
Context context;
public AdminRepository(Context context)
{
this.context = context;
// This seeds the database
Admin admin = new Admin() { Login = "MyLogin" };
this.context.Admins.Add(admin);
this.context.SaveChanges();
}
public Admin GetAdmin(string login)
{
return context.Admins.Where(a => a.Login == login).FirstOrDefault();
}
}
public class EntryRepository
{
Context context;
public EntryRepository(Context context)
{
this.context = context;
}
public void AddEntry(Entry entry){
context.Entrys.Add(entry);
context.SaveChanges();
}
}
Context class:
public class Context : DbContext
{
public Context()
{
Database.SetInitializer<Context>(new DropCreateDatabaseAlways<Context>());
Database.Initialize(true);
}
public DbSet<Admin> Admins { get; set; }
public DbSet<Entry> Entrys { get; set; }
}
Modified Models:
public class Admin
{
public int Id { get; set; }
public string Login { get; set; }
}
public class Entry
{
public int Id { get; set; }
public virtual Admin Admin { get; set; }
}

What Can/Cannot be done inside a Generic repository

I am working on an asp.net mvc-5 with Entity framework 6. now currently i am not using any kind on generic repositories , as the ones mentioned here:-
Link-1
&
Link-2
now the generic repository gives you a feeling that you can do everything in a generic way.. but inside these 2 links seems what can be generilzed are the basic operations for get, add, delete & modify which are by defualt provided inside Entity framework. so can anyone adivce on thses question regading using Generic repositories with EF-6 & MVC-5:-
1.is it really a good approach of using Generic repo ? as seems generic repo will just provide what EF already provide !!
2.let say i have two Parent/Child (DataCenter/Zone) objects:-
public class DataCenter
{
public int ID { get; set; }
public string Name { get; set; }
public byte[] timestamp { get; set; }
public virtual ICollection<Zone> Zones { get; set; }
}
public class Zone
{
public int ZoneID { get; set; }
public string Name { get; set; }
public int DataCenterID { get; set; }
public virtual DataCenter DataCenter { get; set; }
}
now using the Generic repository i can Get,Add,Edit,Delete these 2 objects using the same generic repo methods. but let say i want to retrieve all the Zones related to a specific Datacenter as follow:-
var results = entity.DataCenters.SingleOrDefault(a => a.ID == id).Zones.Where(a => a.Name.Contains("1"));
so can the generic repository support such a query , in a way that i can re-use the query with another object types (other than Datacenter & zones). for example to have a generic query :- to get a parent object by ID and for its child to get the childs that have their names contain the word "1" ?? and what if the parent have multiple child types !! will the generic repository support non-generic queries and operations ?
I went through the same question... I first did a specific repository, then I changed to a generic. But I've ended up having to code so much specific queries, that I decide to change for non-generic repositories, returning ToList (I didn't want IQueryable) and using Unit of Work pattern. Now I think I'm happy with the way things are.
Edit:
Query the child by it's property, bringing back the parent too (is that what you want?):
return await _context.Entity.Include(e => e.Parent)
.Where(e => e.SomeProp == someParam)
.ToListAsync();
Or, Querychild, using some property in the parent, bringing back The parent:
return await _context.Entity.Include(e => e.Parent)
.Where(e => e.Parent.SomeProp == someParam)
.ToListAsync();

Unable to Add Controller (with scaffolding) ASP.NET MVC

This has been addressed in multiple other questions, but alas, I have tried all the solutions posted there with no success. I'm developing an ASP.NET MVC application, using Code-First EF. I am trying to take advantage of the scaffolding built in so that it can automatically create a Controller for me based off my Model and DbContext. However, I am getting the following error when I try to create a Controller in this way:
'Unable to retrieve metadata for Employer.' Using the same DbCompiledModel to create contexts against different types of database servers is not supported. Instead, create a separate DbCompiledModel for each type of server being used.
The code for my model, Employer, my DbContext, MyDataContext, and my web.config file follow:
//Employer.cs
public class Employer : Organization, IEmployer
{
public virtual PhysicalAddress Address { get; set; }
public virtual ICollection<ContactMethod> ContactMethods { get; set; }
public int FederalTaxID { get; set; }
public virtual Client Client { get; set; }
}
-
//MyDataContext.cs
[DbConfigurationType(typeof(MySqlEFConfiguration))]
public class MyDataContext : IdentityDbContext<ApplicationUser>
{
public MyDataContext()
: base("DataConnection")
{
}
static MyDataContext()
{
Database.SetInitializer(new DataInitializer());
}
public DbSet<Client> Clients { get; set; }
public DbSet<Organization> Organizations { get; set; } // Store Employer and OrgEntity
/// <summary>
/// Sets up unclear relationships between entities before the models are constructued in a database.
/// For example, models which extend other models must have their Ids mapped (because it is an
/// inherited member, and so is not found by Entity Framework by default)
/// </summary>
/// <param name="modelBuilder">the object responsible for constructing the database from code-first</param>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//set up parent organization relationship
modelBuilder.Entity<Organization>()
.HasOptional(n => n.ParentOrganization)
.WithMany(o => o.ChildOrganizations)
.Map(m => m.MapKey("ParentOrganization_Id"));
//set up the organization-employee assignment
modelBuilder.Entity<Employee>()
.HasOptional(a => a.OrganizationAssignment)
.WithMany(a => a.Employees);
//used to integrate Identity stuff into same db as Clients, etc.
modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });
}
}
-
<!--web.config-->
.
.
.
<connectionStrings>
<add name="DataConnection"
providerName="Mysql.Data.MysqlClient"
connectionString="server=localhost;user id=myid;password=mypass;persistsecurityinfo=True;database=app_db"/>
</connectionStrings>
.
.
.
From various other posts like this one , this one , and this one . I have tried many different things to get it to work. This includes commenting out my constructor for MyDbContext, changing the providerName attribute of my connection string to be "System.Data.SqlClient", changing the connection string name to be "DefaultConnection", removing my connection strings altogether, and combinations of all of these things. I make sure to rebuild between trying to add the Controller. However, after performing these different changes, I still receive the same error when I try to add a new Controller.
Generally, I can find all my questions already answered, but the answers don't seem to be working for me on this one. I think what might separate my case from the ones linked is that my DbContext is actually an instance of IdentityDbContext. I believe the constructor for IdentityDbContext just calls the base constructor for it, anyway, so I don't see how this could be much of an issue.
Any help is much appreciated. Thank you!
You didn't map your Employer entity to the model. That's why compiler is unable to retrieve metadata for Employer. Firstly, add DbSet as below and let me know, if does it work.
public DbSet<Client> Clients { get; set; }
public DbSet<Organization> Organizations { get; set; }
public DbSet<Employer> Employers { get; set; }

How to prevent referenced object's version column updating when creating a new object in EF 4.1 code first?

I've been trying to map a one-to-many, one-way relationship using EF 4.1 code-first, e.g. User has Address but Address knows nothing about User. This is straightforward to implement either with a ForeignKey attribute or a fluent api (shown in included code).
The problem comes when adding a Version (byte[]) column with a Timestamp attribute on both mapped classes. If we now create an instance of a User that has a reference to an existing (in the db) Address and add it to the context, upon calling SaveChanges a database profiler will show two database queries, one that is the User insert and the other is an update to the Address table to change the Version. Not what I want. If I've modelled no relationship in my domain then I don't want any version change either. I only want a version change on Address if I change an Address instance.
I suspect that because the mapping is using HasMany(), internally the EF DbContext believes there is a collection that needs to be satisified and as the collection has changed (by adding a new User) it automagically updates the version of Address. All this despite the fact Address has no collection property of type ICollection<User>.
So to my question. What mapping do I need to put in place for the relationship to maintain the class structures as they are without having the Address version change when I add a new User?
EDIT:
I've found that the only way I can prevent the update of the version on Address is to reduce the mapping to an HasRequired(a => a.Address) only and no longer have the AddressId foreign key on the User class. It seems if the foreign key "property" is on User either explicit mapping or convention mapping will ensure the version of Address is updated.
I would prefer to apply some extension to HasRequired to tell the context how to treat the relationship rather than having to remove the foreign key property entirely.
Here's the code I use to demonstrate the problem:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
namespace DbTest
{
class Program
{
static void Main(string[] args)
{
Address address = null;
// Make sure we have one address to test with
using (var context = new DemoContext())
{
address = context.Addresses.FirstOrDefault();
if (address == null)
{
address = new Address { Street = "My Street" };
context.Addresses.Add(address);
context.SaveChanges();
}
}
byte[] version = address.Version;
using (var context = new DemoContext())
{
// Uncomment to test attaching
// context.Addresses.Attach(address);
address = context.Addresses.FirstOrDefault();
var user = new User { Name = "Mark", Address = address };
context.Users.Add(user);
context.SaveChanges(); // Results in new user inserted and a version update to the Address referenced object
}
using (var context = new DemoContext())
{
var address2 = context.Addresses.FirstOrDefault();
Console.WriteLine("Versions: {0}, {1}", BitConverter.ToString(version), BitConverter.ToString(address2.Version));
}
Console.ReadLine();
}
}
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public int AddressId { get; set; }
// [ForeignKey("AddressId")]
public Address Address { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
public class Address
{
[Key]
public int AddressId { get; set; }
public string Street { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
public class DemoContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Address> Addresses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasRequired(a => a.Address)
.WithMany()
.HasForeignKey(u => u.AddressId);
}
}
}
A workaround:
Leverage your exposed foreign key and replace this code...
address = context.Addresses.FirstOrDefault();
var user = new User { Name = "Mark", Address = address };
...by:
var addressId = context.Addresses.Select(a => a.AddressId).FirstOrDefault();
var user = new User { Name = "Mark", AddressId = addressId };
This doesn't change the timestamp of the address entity.
I don't think that there is any mapping option to avoid the UPDATE statement of the address in your original code. I would follow your hypothesis that EF considers your code as a change of a relationship between users and address and therefore updates the address, no matter if the user collection is exposed in the Address model or not.
I've found that the only way I can prevent the update of the version on Address is to reduce the mapping to an HasRequired(a => a.Address) only and no longer have the AddressId foreign key on the User class. It seems if the foreign key "property" is on User either explicit mapping or convention will ensure the version of Address is updated.
I would prefer to apply some extensino to HasRequired to tell the context how to treat the relationship rather than having to remove the foreign key property entirely.

Resources