Retrieving data for logged in user - asp.net-core

What is the best way to retrieve data for a logged in user in Api. I can retrieve data for all users just fine.
'ApplicationDbContext':
public DbSet<Category> Categories { get; set; }
'ApplicationUser':
public ICollection<Category> Categories { get; set; }
my Repository:
public class CategoryRepository : ICategoryRepository
{
private ApplicationDbContext _ctx;
public CategoryRepository(ApplicationDbContext ctx)
{
_ctx = ctx;
}
//to do
public IEnumerable<Category> GetAll(string username)
{
return _ctx.Users.First(...).Categories.Include(x => x.Tasks);
}
public IEnumerable<Category> GetAllForFromAllUsers()
{
return _ctx.Categories.Include(x => x.Tasks);
}
}
As you have noticed above, I can not use extension method 'Include' on ICollection.
What is the best way to retrieve data for a specific user?

Can you try this?
var users = _ctx.Users
.Include(user => user.Categories)
.ThenInclude(cat => cat.Tasks)
.Where(u => u.username == "xx")
.ToList();
From Loading Related Data

Related

.NET Core many-to-many query

I have a many to many realtionship between a Project-model an a user. I'm trying to display all projects in which the logged in user is a member of, but I can't really get it right.
User Model
public class AppUser : IdentityUser
{
public ICollection<UserProject> UserProjects { get; set; }
}
Project Model
public class Project
{
public int ProjectId { get; set; }
public string ProjectName { get; set; }
public ICollection<UserProject> UserProjects { get; set; }
}
UserProject Model
public class UserProject
{
public string UserId { get; set; }
public AppUser AppUser { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
}
I have tried to write the query in SQL like this but it doesn't work
AppUser user = await userManager.GetUserAsync(HttpContext.User);
var q = (from e in context.Projects
join t in context.UserProjects on e.ProjectId equals t.ProjectId
where t.UserId == user.Id
select e.ProjectName);
I have managed to write a query to get all the projects by an id and all it's members like this
var project = context.Projects
.Where(x => x.ProjectId == id)
.Include(x => x.UserProjects)
.ThenInclude(x => x.AppUser)
.First();
But I don't manage to get all the projects a user is a member of. I appriciate any help I can get, Thanks!
You can get all the projects a user is a member of like :
var userID = User.Claims.FirstOrDefault(x=>x.Type== ClaimTypes.NameIdentifier).Value;
var projects = _context.Users
.Where(p => p.Id == userID)
.SelectMany(p => p.UserProjects)
.Select(pc => pc.Project).ToList() ;
In a many-to-many relationship :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<UserProject>()
.HasKey(pc => new { pc.UserId, pc.ProjectId });
modelBuilder.Entity<UserProject>()
.HasOne(pc => pc.AppUser)
.WithMany(p => p.UserProjects)
.HasForeignKey(pc => pc.UserId);
modelBuilder.Entity<UserProject>()
.HasOne(pc => pc.Project)
.WithMany(c => c.UserProjects)
.HasForeignKey(pc => pc.ProjectId);
}

EF Core tries to add new record instead of update 1:many relation

I'm not sure why while trying to create an entity which is 1:many
EF tries to add new entry in Asp Net Users instead of update 1:many
I have one user which has many items
SqlException: Violation of PRIMARY KEY constraint 'PK_AspNetUsers'. Cannot insert duplicate key in object 'dbo.AspNetUsers'. The duplicate key value is (cdbb1f2f-ddcf-40c0-97ec-f50f8049d87a).
public class Context : IdentityDbContext
{
public Context(DbContextOptions options) : base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Item> Items { get; set; }
public DbSet<File> Files { get; set; }
}
public class User : IdentityUser
{
public List<Item> Items { get; set; } = new List<Item>();
}
public class Item
{
private Item()
{
}
public Item(string title, User owner, File file)
{
Title = title;
Owner = owner;
File = file;
}
public int Id { get; private set; }
public string Title { get; set; }
public User Owner { get; set; }
public File File { get; set; }
public DateTime CreationDate { get; } = DateTime.Now;
}
And here's where's the problem:
var fileResult = await _file.SaveFile(input.File);
var item = new Item(input.Title, user, fileResult.File);
user.Items.Add(item);
await _context.Items.AddAsync(item);
await _context.SaveChangesAsync();
User is loaded with:
public User GetUser()
{
return _context.Users.FirstOrDefault(x => x.UserName == _http.HttpContext.User.Identity.Name);
}
I tried that:
When I change
public Item(string title, User owner, File file)
{
Title = title;
Owner = owner;
File = file;
}
to just:
public Item(string title, File file)
{
Title = title;
File = file;
}
and let it be handled by:
user.Items.Add(item);
then OwnerId in DB is null
Using:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasMany(x => x.Items)
.WithOne(x => x.Owner);
modelBuilder.Entity<Item>().HasOne(x => x.Owner);
}
is not helping either
The problem was casued by ServiceLifetime of DbContext
Because User was loaded in Controller and then thrawn into Service that was responsible for business logic
I changed
(options => options.UseSqlServer(Configuration["Database:ConnectionString"]), ServiceLifetime.Transient);
to
(options => options.UseSqlServer(Configuration["Database:ConnectionString"]), ServiceLifetime.Scoped);
and it works fine.

Mapping Id to Model for controller API

I'm using asp.net core on a project. (I'm fairly new to it)
I have a User Model. the code below is a simplified version:
public class User
{
public int id { get; set; }
// attribute declaration
public ICollection<User> friends { get; set; }
}
I'm using automapper service to map my api to this Model:
public class UserResource
{
public UserResource()
{
this.friendsId = new List<int>();
}
public int id { get; set; }
// attribute declaration
public ICollection<int> friendsId { get; set; }
}
consider a post request to UserController with the following body:
{
"id" : 1
"friendsId": [2,3,4],
}
I want to map integers in friendsId to id of each user in friends collection. but I can't figure out what to do. here's what I've got:
CreateMap<UserResource,User>()
.ForMember(u => u.friends,opt => opt.MapFrom(????);
is this the right approach? if so how should I implement it?
or should I change my database model to this:
public class User
{
public int id { get; set; }
// attribute declaration
public ICollection<int> friendsId { get; set; }
}
Thank you in advance.
You'll need to implement a custom value resolver. These can be injected into, so you can access things like your context inside:
public class FriendsResolver : IValueResolver<UserResource, User, ICollection<User>>
{
private readonly ApplicationDbContext _context;
public FriendsResolver(ApplicationDbContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
}
public ICollection<User> Resolve(UserResource source, User destination, ICollection<User> destMember, ResolutionContext context)
{
var existingFriendIds = destMember.Select(x => x.Id);
var newFriendIds = source.friendsId.Except(existingFriendIds);
var removedFriendIds = existingFriendIds.Except(source.Friends);
destMember.RemoveAll(x => removedFriendIds.Contains(x.Id);
destMember.AddRange(_context.Users.Where(x => newFriendIds.Contains(x.Id).ToList());
return destMember;
}
}
Not sure if that's going to actually work as-is, as I just threw it together here, but it should be enough to get your going. The general idea is that you inject whatever you need into the value resolver and then use that to create the actual stuff you need to return. In this case, that means querying your context for the User entities with those ids. Then, in your CreateMap:
.ForMember(dest => dest.friends, opts => opts.ResolveUsing<FriendsResolver>());
This only covers one side of the relationship, though, so if you need to map the other way, you may need a custom resolver for that path as well. Here, I don't think you actually do. You should be able to just get by with:
.ForMember(dest => dest.friendsId, opts => opts.MapFrom(src => src.friends.Select(x => x.Id));
This would help
CreateMap<UserResource,User>()
.ForMember(u => u.friends,opt => opt.MapFrom(t => new User {FriendsId = t.friendsId);
public class User
{
...
public ICollection<User> friends { get; set; }
}
Where friends is ICollection<User> whereas UserResource class has ICollection<int>. There is type mismatch here. You need to map ICollection to ICollection that is why I casted new User ...

EF Core Table does not exist when joining from multiple DbContext

I am facing an issue when trying to join 2 tables from 2 DbContext which represents different databases.
I am using Fluent API to create foreign key relationship between 2 tables.
Here are the Dbcontext configuration and models.
public class DbContextDemo1: DbContext
{
public DbSet<Agency> Agencies { get; set; }
public DbContextDemo1(DbContextOptions<DbContextDemo1> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("db1")
.Entity<Agency>()
.ToTable("agencies")
.HasKey(agency => agency.Id)
.HasOne(agency => agency.AgencyApp)
.WithOne(app => app.Agency)
.HasForeignKey<Agency>(agency => agency.Id);
}
}
public class DbContextDemo2: DbContext
{
public DbSet<AgencyApp> AgencyApps { get; set; }
public DbContextDemo2(DbContextOptions<DbContextDemo2> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("db2")
.Entity<AgencyApp>()
.ToTable("agenciesapps")
.HasKey(app => app .Id)
.HasOne(app=> app.Agency)
.WithOne(agency => agency.AgencyApp)
.HasForeignKey<AgencyApp>(app=> app.AgencyId);
}
}
Here are the Models:
public class Agency
{
public Guid Id { get; set; }
public AgencyApp AgencyApp { get; set; }
}
public class AgencyApp
{
public Guid Id { get; set; }
public Guid AgencyId { get; set; }
public Agency Agency { get; set; }
}
Now, When I try to get Agencies data along with AgencyApp.
var result = _dbContextDemo1.Agencies.Include(agency => agency.AgencyApplication)
It throws an error
"Table 'db2.agenciesapps' doesn't exist".
I can see in server console it is doing inner join between these two tables.
Help would be hightly appreciated. Thanks
You cannot join across databases. You'll have to use two separate queries:
var agencies = await _dbContextDemo1.Agencies.ToListAsync();
var agencyApps = await _dbContextDemo2.AgencyApps.Where(x => agencies.Select(a => a.Id).Contains(x.AgencyId)).ToListAsync();
Note: Since you're selecting all agencies, you can technically just select all agency applications as well, but filtering by the ids of the agencies that were selected works better if you end up filtering that set as well.
Then you can map the data from the second query over:
agencies.ForEach(x => x.AgencyApp = agencyApps.SingleOrDefault(a => a.AgencyId == x.Id));
Including, or joining, tables from different contexts are not supported because
the contexts can be connected to different db servers.
Instead of using different context, add the entities to the same context (Why would you even want to have two different context for them?)
public class DbContextDemo1: DbContext
{
public DbSet<Agency> Agencies { get; set; }
public DbSet<AgencyApp> AgencyApps { get; set; }
public DbContextDemo1(DbContextOptions<DbContextDemo1> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("db1")
.Entity<Agency>()
.ToTable("agencies")
.HasKey(agency => agency.Id)
.HasOne(agency => agency.AgencyApp)
.WithOne(app => app.Agency)
.HasForeignKey<Agency>(agency => agency.Id);
modelBuilder.HasDefaultSchema("db1")
.Entity<AgencyApp>()
.ToTable("agenciesapps")
.HasKey(app => app .Id)
.HasOne(app=> app.Agency)
.WithOne(agency => agency.AgencyApp)
.HasForeignKey<AgencyApp>(app=> app.AgencyId);
}
}
If you really need to have them on two different contexts then you need to fetch all entities to memory and then join them together (this is not a good idea as you need to fetch all agencies to memory)
var agencies = _dbContextDemo1.Agencies.ToList();
foreach(var agency in agencies)
{
agency.AgencyApps = _dbContextDemo2.AgencyApps.FirstOrDefault(a=> a.AgencyId == agency.Id);
}

After updating to nHibernate 4, cannot use ManyToMany Mappings. Could not determine type

After updating to nHibernate (4.0.2.4000 via nuget), the many to many mappings that previously worked now cause mapping exception "Could not determine type for: nHibernateManyToMany.IRole, nHibernateManyToMany, for columns: NHibernate.Mapping.Column(id)"
Seems to be only for many to many, and when the List is a interface (i.e. List<Role> vs List<IRole>).
Example code that now fails:
class Program
{
static void Main(string[] args)
{
var configuration = new Configuration().SetProperty(Environment.ReleaseConnections, "on_close")
.SetProperty(Environment.Dialect, typeof(SQLiteDialect).AssemblyQualifiedName)
.SetProperty(Environment.ConnectionDriver, typeof(SQLite20Driver).AssemblyQualifiedName)
.SetProperty(Environment.CollectionTypeFactoryClass, typeof(DefaultCollectionTypeFactory).AssemblyQualifiedName)
.SetProperty(Environment.CommandTimeout, "0");
var mapper = new ModelMapper();
mapper.AddMappings(new[] { typeof(EmployeeMapping), typeof(RoleMapping) });
var hbmMapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
hbmMapping.autoimport = false;
configuration.AddMapping(hbmMapping);
// this line will fail
var factory = configuration.BuildSessionFactory();
}
}
public class Employee
{
public virtual int Id { get; set; }
public virtual List<IRole> Roles { get; set; }
}
public interface IRole
{
int Id { get; set; }
string Description { get; set; }
}
public class Role : IRole
{
public virtual int Id { get; set; }
public virtual string Description { get; set; }
}
public class EmployeeMapping : ClassMapping<Employee>
{
public EmployeeMapping()
{
Id(c => c.Id, x =>
{
x.Type(NHibernateUtil.Int32);
x.Generator(Generators.Identity);
x.Column("EmployeeId");
});
Bag(x => x.Roles, m =>
{
m.Table("EmployeeRole");
m.Key(km =>
{
km.Column("EmployeeId");
km.NotNullable(true);
km.ForeignKey("FK_Role_Employee");
});
m.Lazy(CollectionLazy.Lazy);
}, er => er.ManyToMany(m =>
{
m.Class(typeof(Role));
m.Column("RoleId");
}));
}
}
public class RoleMapping : ClassMapping<Role>
{
public RoleMapping()
{
Id(c => c.Id, x =>
{
x.Type(NHibernateUtil.Int32);
x.Generator(Generators.Identity);
x.Column("RoleId");
});
Property(x => x.Description, c =>
{
c.Length(50);
c.NotNullable(true);
});
}
}
Any help or suggestions about where we could look for details on how this has changed since v3 would be appreciated.
Turned out to be a bug in nHibernate.
A pull request has been submitted here https://github.com/nhibernate/nhibernate-core/pull/385