I am working on .NET Core application with Entity Framework core 2.1 and auto-mapper 3.2. In my following script, I am getting null value and get an error when I am trying to ProjectTo(myviewClass). Now I know I can check individual value in following script by do userRoleList.select(x=>x.ConsultationId) == null? null: projectTo but the issue is, its collection so how do I apply similar logic as you can see I am getting a collection of consultation records where its ConsultationId contains in userRoleLits.
LINQ
(from user in Context.Users
join userRole in Context.UserRoles on user.Id equals userRole.UserId into userRoleList
where (user.Id == (UserId ?? user.Id))
select new UsersWithAccessProfileDataView
{
UserId = user.Id,
UserName = user.Name,
Consultation = Context.Consultations.Where(x => userRoleList.Select(y => y.ConsultationId).Contains(x.Id)).ProjectTo<ConsultationDataView>() //need help here
}).OrderBy(x => x.UserName);
model view class
public class UsersWithAccessProfileDataView
{
public Guid UserId { get; set; }
public string UserName { get; set; }
public IQueryable<ConsultationDataView> Consultation { get; set; }
}
Related
I receive an exception when trying to replace the owned collection with the one I receive through API (EF Core 6).
System.InvalidOperationException: 'The instance of entity type 'Product' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Here is my model configuration:
public class Order
{
public int Id { get; set; }
public ICollection<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public Product Product { get; set; }
public int Quantity { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
modelBuilder.Entity<Product>(x =>
{
x.HasKey(e => e.Id);
x.Property(e => e.Name);
});
modelBuilder.Entity<Order>(x =>
{
x.HasKey(e => e.Id);
x.OwnsMany(e => e.OrderItems, p =>
{
p.ToTable("OrderItems")
.WithOwner();
p.HasOne(e => e.Product).WithMany();
p.Property(e => e.Quantity);
});
});
Act:
//these are updated items received through API
var newItems = new List<OrderItem>
{
new OrderItem{ Product = new Product { Id = 2}, Quantity = 2},
new OrderItem{ Product = new Product { Id = 3}, Quantity = 3}
};
var existingOrder = _dbContext.Orders
.Include(o => o.OrderItems).ThenInclude(i => i.Product)
.Single(o => o.Id == 1);
// Existing OrderItems are already referencing Product with Id 2
existingOrder.OrderItems = newItems;
// Error occurs because Product with Id = 2 was already tracked
_dbContext.SaveChanges();
I know that the issue might be resolved by replacing Product with ProductId within my OrderItem class but I would rather prefer to keep my domain model as it is.
What is the best practice to perform such update?
it's all about understanding of the modeling. You can create a model response to update also, but just have to update manually by calling it.
you have existingOrder from the query include, then include like this I guess
var existingOrder = ...
existingOrder.OrderItems.Update(yourentity_OrderItem);
await _dbContext.SaveChangesAsync();
So this mean you have to retrieve each Product.id by a for, and update each new items, then savechanges.
I have two nested classes: Partner contains a Company (that has a field "Name")
I do a search by Id on the partner's Id
I want to do a search on the company's "Name" field
here is my poco:
public class Partner
{
[Required]
public int? Id { get; set; }
[Required]
public Company Company { get; set; }
using AsQueryable, I can then stack filters one by one
I try to have a query that joins the second table to do a search on that entity's name field
public DbSet<Partner> Partners { get; set; }
...
var data = _context.Partners.AsQueryable();
if (partnersSearch.SearchById != null)
{
data = data.Where(p => p.Id == partnersSearch.SearchById.GetValueOrDefault());
}
if (partnersSearch.SearchByName != null)
{
data = data.Include(a => a.Company.Select(b => b.Name = partnersSearch.SearchByName));
but for the join between the tables, the last line cannot compile
it complains that Company does not contain a definition of has no Select
what am I doing wrong ?
thanks for helping me on this
If you try a where after your include. Does that help?
data.Include(a => a.Company).Where(partner=>partner.Company.Name.equals(partnersSearch.SearchByName))
We have this edit Razor pages (edit.cshtml) which is extended from the following page model and it's very basic only include the PopulateRolesDropDownList:
public class RoleNamePageModel : PageModel
{
public SelectList RoleNameSL { get; set; }
public void PopulateRolesDropDownList(ApplicationDbContext _context,
object selectedRole = null)
{
var rolesQuery = from d in _context.Roles
orderby d.Name // Sort by name.
select d;
RoleNameSL = new SelectList(rolesQuery,
"RoleId", "Name", selectedRole);
}
}
Also in this Edit page, we added:
<input type="hidden" asp-for="User.UserRoles.ElementAt(0).RoleId" name="User.Current.RoleId" />
We also do the [BindProperty] in the code behind
public ApplicationUser User { get; set; }
We need to find out whether there is a change on this model. What is the approach to do this?
ENVIRONMENT:
.NET Core 2.2
Razor Pages
UPDATE - 1:
On the PostAsync, we made another call to the database:
var userRoleToUpdate = await _context.UserRoles
.FirstOrDefaultAsync(m => m.UserId == id.ToString());
We just need to compare this value with the change on a drop-down list or not. We could not work how.
UPDATE - 2:
We did change as per recommend by #NevilleNazerane below:
public class AssignClubUserViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public Guid SelectedRoleID { get; set; }
}
[BindProperty]
public AssignClubUserViewModel AssignClubUser { get; set; }
and added OnGetAsync:
public async Task<IActionResult> OnGetAsync(Guid? id)
{
if (id == null)
return NotFound();
var user = await _context.Users
.Include(u => u.ClubApplicationUsers)
.FirstOrDefaultAsync(m => m.Id == id.ToString());
AssignClubUser.FirstName = user.FirstName;
AssignClubUser.LastName = user.LastName;
AssignClubUser.UserName = user.UserName;
AssignClubUser.SelectedClubID =
user.ClubApplicationUsers.ElementAt(0).ClubID;
....
Is this right? I got the error: NullReferenceException: Arg_NullReferenceException on line AssignClubUser.FirstName = user.FirstName;
UPDATE - 3:
Fixed by creating a ModemView and then on the OnGetAsync() for query ensure to mapped with the ModelView:
var user = await _context.Users
.Include(u => u.ClubApplicationUsers)
.Where(t => t.Id == id.ToString())
.Select(t => new AssignClubUserViewModel<ApplicationUser>
{
FirstName = t.FirstName,
LastName = t.LastName,
UserName = t.UserName,
SelectedClubID = t.ClubApplicationUsers.ElementAt(0).ClubID
}).SingleAsync();
Since you have a view model, I recommend you simplify your bindings and let your behind code handle the other functionalities. You can first make a SelectedRoleId property:
public int SelectedRoleId { get; set; }
In your view model, you can assign this property's default value to User.UserRoles.ElementAt(0).RoleId in either your constructor or your OnGet, based on how you need it set up. This way the drop down is bound to a simple property.
For binding dropdowns (HTML selects) .NET Core provides the asp-items tag helper.
<select asp-for="SelectedRoleId" asp-items="Model.RoleNameSL"></select>
In your OnPostAsync, you can use SelectedRoleId to access the selected value.
In Entity Framework, I would like to get one object which includes a list, but list gets only first record.
I have 2 objects Sale and Profile, they are different from database objects, I create these objects in query like "select new Sale { }". Profile object contains Sale type list. When query executed, list gets just first record in database.
Sale Complex Object
public class Sale
{
public int Id { get; set; }
public string Header { get; set; }
public double Price { get; set; }
}
Profile Complex Object
public class Profile
{
public int Id { get; set; }
public string Name { get; set; }
public List<Sale> SalesList { get; set; }
}
I use left join because it should insert this object to list, if next object is null.
Query Here
Profile profile = (from u in db.USER
join s in db.SALE on u.ID equals s.USER_ID into saleleft
from salej in saleleft.DefaultIfEmpty()
where u.ID == _userId
select new Profile
{
Id = u.ID,
Name = u.NAME,
SalesList= new List<Sale>()
{
salej != null ? new Sale
{
Id=postj.ID,
Header=salej.HEADER,
Price=salej.PRICE
} : null
}.ToList()
}).FirstOrDefault();
I guess this can be about FirstOrDefault() method. Hence I think it should get all records to SalesList. How can I get all records to list? Any idea?
Thanks in advance.
I think you need to use group here. Could you try this and let me know if it works?
// didn't test the code
Profile profile = (from u in db.USER
join s in db.SALE on u.ID equals s.USER_ID into saleleft
where u.ID == _userId
from salej in saleleft.DefaultIfEmpty()
group salej by new { u.ID, u.NAME } into g
select new Profile
{
Id = g.Key.ID,
Name = g.Key.NAME,
SalesList = g.Select( x => new Sale { Id = postj.ID, Header = x.HEADER, Price = x.PRICE }).ToList()
}).FirstOrDefault();
Btw, what is postj?
I have a project where I'm using EF5, I made a custom Guid Generator and I have an override of the SaveChanges method to assign the ids of my entities.
Everything is working fine except in one case: when the ID of one entity is a FK to another ID of another entity.
A little bit of code to explain the problem:
I have two entities I cannot change:
public class FixedEntityA
{
public Guid Id { get; set;}
public string SomeText { get; set; }
}
public class FixedEntityB
{
public Guid Id { get; set;}
public int OneInt { get; set; }
}
In my project I have an entity defined like this:
public class ComposedEntity
{
public Guid Id { get; set;}
public FixedEntityA FixedA { get; set; }
public FixedEntityB FixedB { get; set; }
public double OneDouble { get; set; }
}
The relationships are:
ComposedEntity may have 0 or 1 FixedEntityA
ComposedEntity may have 0 or 1 FixedEntityB
The constraints on the id are:
The Id of FixedEntityA is a FK pointing to the Id of ComposedEntity
The Id of FixedEntityB is a FK pointing to the Id of ComposedEntity
The mapping class are:
public ComposedEntity(): EntityTypeConfiguration<ComposedEntity>
{
HasOptional(fea => fea.FixedA).WithRequired();
HasOptional(feb => feb.FixedB).WithRequired();
}
Here is my SaveChanges override:
foreach (var entry in ChangeTracker.Entries<IEntity>().Where(e => e.State == EntityState.Added))
{
Type t = entry.Entity.GetType();
List<DatabaseGeneratedAttribute> info = t.GetProperty("Id")
.GetCustomAttributes(typeof (DatabaseGeneratedAttribute), true)
.Cast<DatabaseGeneratedAttribute>().ToList();
if (!info.Any() || info.Single().DatabaseGeneratedOption != DatabaseGeneratedOption.Identity)
{
if (entry.Entity.Id == Guid.Empty)
entry.Entity.Id = (Guid) _idGenerator.Generate();
}
}
return base.SaveChanges();
This code works fine everywhere for all kind of relationships except in this case, I am missing a test to make sure I'am not setting an id on id that are foreign keys, and I have no clue on how to check if an Id is a FK...
Here is a sample object where this code fails:
var fea = new FixedEntityA();
var feb = new FixedEntityB();
var composedEntity = new ComposedEntity();
composedEntity.FixedA = fea;
composedEntity.FixedB = feb;
If you insert the whole graph, all three objects are marked as Added and all Ids are default.
The problem is, with the current SaveChanges method, I will go through all object with the Added state in the change tracker and I will assign an Id to all entity with a default Guid and break my FK constraints.
Thanks in advance guys!
Here is some code that will get the FK properties for a given type (it's horrible I know). Should be simple enough to plug this into your code.
var typeName = "Category";
var fkProperties = ((IObjectContextAdapter)db)
.ObjectContext
.MetadataWorkspace
.GetItems<AssociationType>(DataSpace.CSpace)
.Where(a => a.IsForeignKey)
.Select(a => a.ReferentialConstraints.Single())
.Where(c => c.FromRole.GetEntityType().Name == typeName)
.SelectMany(c => c.FromProperties)
.Select(p => p.Name);