I have ViewModel
UserGroup.cs
public class UserGroup
{
public User User { get; set; }
public List<Group> Groups { get; set; }
}
And I want to have the User and all related Groups. The problem is that the query returns duplicates of all users if there is more than 1 group related to him (and It's expected of course). The first thing that comes to my mind is to fetch all the users with single query and then foreach user to get the groups related to him and push it in the list. But I'm looking for a better way (if there is) to do that.
The relationship is made with junction table.
SQL Query
SELECT u.UserName, ug.GroupName FROM auth.UserUserGroup uug
INNER JOIN [auth].[User] u ON u.UserId = uug.UserId
INNER JOIN auth.UserGroup ug ON ug.UserGroupId = uug.UserGroupId
I'm sure someone will have a more elegant solution, but I've used Linq to construct a tiered object a number of times
var dbRecords = repository.Get(userId); // However you are getting your records, do that here
var result = dbRecords.GroupBy(x => x.UserId)
.Select(g => new UserGroup
{
User = new User { UserName = g.First().UserName },
Groups = g.Select(y => new Group { GroupName = y.GroupName }
}
);
I've tried to use your object names. Hopefully I didn't make any mistakes
Related
I wants to join for table from sql with lambda code
i use list for any table and join them
in one table i have "name" and "family" that i need to concat them to veiw fullname in datagrid view but i cat do that
Please guide me and if there is a better solution, I would appreciate it
my code :
var listPartner = db.BusinessPartnerRepository.GetAllPartners();
var listEvidence = db.Evidence.Get();
var listDetail = db.EvidenceDetail.Get();
var listCountry = db.Country.Get();
var result = (from evidence in listEvidence
join detail in listDetail
on evidence.Id equals detail.EvidenceId
join countryname in listCountry
on evidence.CountryId equals countryname.Id
join partner in listPartner
on evidence.PartnerId equals partner.Id
select new
{
evidence.TruckNo,
evidence.SerialNo,
evidence.Date,
partner.Name.Concat(" "+partner.Familly).ToString() ,
detail.Id,
detail.EvidenceId,
detail.MerchandiseName,
detail.weight,
detail.Unit,
detail.Discription,
countryname.CountryName
}).ToList();
dgEvidenceList.AutoGenerateColumns = false;
dgEvidenceList.DataSource = result;
the code of "Get" method:
public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> where = null)
{
IQueryable<TEntity> query = _dbSet;
if (where != null)
{
query = query.Where(where);
}
return query.ToList();
}
the code of "GetAllPartners" method:
public List<BusinessPartners> GetAllPartners()
{
return db.BusinessPartners.ToList();
}
Relation:
the "Evidence" entity have feilds that be foreignKey:
PartnerId ---> Pk in BusinessPartner
CountryId ---> Pk in Country
evidenceId --> Pk in Evidence
You seem to be quite new to C# and Entity Framework. I would advise looking up a few guides.
I am not quite sure if I understand what is your intent. I will try to answer your question as best as I can. Since you are using Entity Framework, you should be interacting with your tables using LINQ and the extension methods entity framework provides. It will make your life easier.
I assume the definition of your class Evidence looks similar to this:
public class Evidence
{
//...
public Detail Detail { get; set; }
public Country Country { get; set; }
public Partner Partner { get; set; }
//...
}
You can then use Include to join your tables together:
List<Evidence> evidences = db.Evidence
.Include(e => e.Detail)
.Include(e => e.Country)
.Include(e => e.Partner).ToList();
You should be able to then use this list of Evidences as data for your DataGrid. However, if you want/need to flatten it to a table of strings, you can do this:
List<List<string>> table = evidences.Select(e => new List<string>()
{
e.TruckNo,
e.SerialNo,
//...
}).ToList();
If you need to join two strings together with a space between, you have many options:
// standard string concatenation
string1 + " " + string2
// string interpolation
$"{string1} {string2}"
I hope I answered your question. Again, I really recommend looking up some C# and Entity Framework guides.
I have this code:
private IQueryable<Trip> GetTripWithBreaksAndPassengers(DB_Context context, long id)
{
return from t in context.trip
where t.Id == id
join breaks in context.breaks on t.Id equals breaks.tripid into breaksJoin
join drivers in context.drivers on t.Id equals drivers.tripid into driversJoin
select new Trip() { TripBreaks = ?, TripDrivers = ?};
}
For my Trip specified by an id, I want to return a list of Breaks and Drivers.
My Trip object has two fields which are lists, TripBreaks and TripDrivers:
public virtual List<TripBreak> TripBreaks { get; set; }
public virtual List<TripDriver> TripDrivers { get; set; }
I want both of them to be returned as part of a Trip - I am expecting breaksJoin and driversJoin to hold those specific results, but if queried like
TripDrivers = driversJoin.ToList()
it will throw an error.
How should I use those join results to get the elements held?
Explicit joins in a EF project are a code smell.
You should be able to use navigation properties and Include() to get the data you want, something like this.
var result = context.trip.Include(t => t.Breaks).Include(t => t.Drivers).FirstOrDefault(t => t.Id == id);
This will get you all the related entities in one go.
Adjust property names accordingly, since you didn't share your model classes.
I am trying to use anonymous types in Entity Framework, but I am getting an error about
Unable to create a constant value
MinQty and MaxQty are int so I don't know if I need to add to Convert.ToInt32?
Unable to create a constant value of type 'Anonymous type'. Only primitive types or enumeration types are supported in this context.
This builds a list object
var listOfLicense = (from l in db.License
select new
{
l.ProductId,
l.MinLicense,
l.MaxLicense
}).tolist();
This is the larger EF object where I am getting the error am I missing a casting?
var ShoppingCart = (from sc in db.ShoppingCarts
Select new model.Shoppingchart{
ShoppingCartId= sc.Id,
MinQty = (int)listOfLicense
.Where(mt => (int)mt.ProductId == sc.ProductId)
.Select(mt => (int)mt.MinLicense)
.Min(mt => mt.Value),
MaxQty = (int)listOfLicense
.Where(mt => (int)mt.ProductId == p.ProductId)
.Select(mt =>(int) mt.MaxQty)
.Max(mt => mt.Value)}.tolist();
This builds a list object
var listOfLicense = (from l in db.License
select new
{
l.ProductId,
l.MinLicense,
l.MaxLicense
})
The above example does not build a list of objects. It builds a query to return objects of that anonymous type.
This builds an in-memory list of objects of that type:
var listOfLicense = (from l in db.License
select new
{
l.ProductId,
l.MinLicense,
l.MaxLicense
}).ToList();
Using .ToList() here will execute the query and return a materialized list of the anonymous types. From there, your code may work as expected without the exception. However, this is effectively loading the 3 columns from all rows in your database table, which may be a problem as the system matures and rows are added.
The error you are getting isn't a casting issue, it is a translation issue. Because your initial query is still just an EF Query, (IQueryable) any further querying against it will need to conform to EF limitations. EF has to be able to translate what your expressions are trying to select back into SQL. In your case, what your real code is trying to do is breaking those rules.
Generally it is better to let EF work with the IQueryable rather than materializing an entire list to memory. Though to accomplish that we'd need to either see the real code, or a minimum reproducible example.
This code:
MinQty = (int)listOfLicense
.Where(mt => (int)mt.ParentProductId == p.ProductId)
.Select(mt => (int)mt.MinLicense)
.Min(mt => mt.Value),
... does not fit with the above anonymous type as there is no correlation between what mt.ParentProductId is in relation to the anonymous type. (p seems to be associated with that type, not mt so there looks to be a lot of Query code missing from your example.)
Edit: based on your updated example:
var ShoppingCart = (from sc in db.ShoppingCarts
Select new model.Shoppingchart{
ShoppingCartId= sc.Id,
MinQty = (int)listOfLicense
.Where(mt => (int)mt.ProductId == sc.ProductId)
.Select(mt => (int)mt.MinLicense)
.Min(mt => mt.Value),
MaxQty = (int)listOfLicense
.Where(mt => (int)mt.ProductId == p.ProductId)
.Select(mt =>(int) mt.MaxQty)
.Max(mt => mt.Value)}.ToList();
It may be possible to build something like this into a single query expression depending on the relationships between ShoppingCart, Product, and Licence. It almost looks like "Licence" really refers to a "Product" which contains a min and max quantity that you're interested in.
Assuming a structure like:
public class Product
{
[Key]
public int ProductId { get; set; }
public int MinQuantity { get; set; }
public int MaxQuantity { get; set; }
// ...
}
// Here lies a question on how your shopping cart to product relationship is mapped. I've laid out a many-to-many relationship using ShoppingCartItems
public class ShoppingCart
{
[Key]
public int ShoppingCartId { get; set; }
// ...
public virtual ICollection<ShoppingCartItem> ShoppingCartItems { get; set; } = new List<ShoppingCartItem>();
}
public class ShoppingCartItem
{
[Key, Column(0), ForeignKey("ShoppingCart")]
public int ShoppingCartId { get; set; }
public virtual ShoppingCart ShoppingCart{ get; set; }
[Key, Column(1), ForeignKey("Product")]
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
With something like this, to get shopping carts with their product min and max quantities:
var shoppingCarts = db.ShoppingCarts
.Select(sc => new model.ShoppingCart
{
ShoppingCartId = sc.Id,
Products = sc.ShoppingCartItems
.Select(sci => new model.Product
{
ProductId = sci.ProductId,
MinQuantity = sci.MinQuantity,
MaxQuantity = sci.MaxQuantity
}).ToList()
}).ToList();
This would provide a list of Shopping Carts with each containing a list of products with their respective min/max quantities.
If you also wanted a Lowest min quantity and highest max quantity across all products in a cart:
var shoppingCarts = db.ShoppingCarts
.Select(sc => new model.ShoppingCart
{
ShoppingCartId = sc.Id,
Products = sc.ShoppingCartItems
.Select(sci => new model.Product
{
ProductId = sci.ProductId,
MinQuantity = sci.MinQuantity,
MaxQuantity = sci.MaxQuantity
}).ToList(),
OverallMinQuantity = sc.ShoppingCartItems
.Min(sci => sci.MinQuantity),
OverallMaxQuantity = sc.ShoppingCartItems
.Max(sci => sci.MaxQuantity),
}).ToList();
Though I'm not sure how practical a figure like that might be in relation to a shopping cart structure. In any case, with navigation properties set up for the relationship between your entities, EF should be perfectly capable of building an IQueryable query for the data you want to retrieve without resorting to pre-fetching lists. One issue with pre-fetching and re-introducing those lists into further queries is that there will be a maximum # of rows that EF can handle. Like with SQL IN clauses, there is a maximum # of items that can be parsed from a set.
In any case it sounds like it's provided you with some ideas to try and get to the figures you want.
I have a Activities model as follows
public class Activity
{
// this is email
public string CreatedBy {set;get;}
// Relationship
public ApplicationUser User{set;get;}
}
Then I have the User model:
public class ApplicationUser
{
// ID from Identity
public string Id{set;get;}
public string Email {set;get;}
}
Of course I have the corresponding tables in the database.
What i need to find out is the users who didnt do any activity.
While the code below works, it is not efficient and times out. Because I have 500K activity in the Activities table.
var userz = _db.Users.AsNoTracking();
var groupedUsers = _db.Activities.AsNoTracking().GroupBy(x => x.CreatedBy).Select(group => new { CreatedBy = group.Key, Count = 1 }).Select(x=> x.CreatedBy);
var result = userz.Where(x => groupedUsers.Contains(x.Email) == false);
I tried the same Query for Navigation property, which is indexed, ie: User above. Yet the query times out.
Is there a more efficient solution for this using left join?
You should be better of with foreign keys but if this is really how your classes look you could try
_db.Users.Where(u => !_db.Activities.Any(u => a.ApplicationUser == u));
I am trying to get the latest contact with a given user, grouped by user:
public class ChatMessage
{
public string SentTo { get; set; }
public string SentFrom { get; set; }
public string MessageBody { get; set; }
public string SendDate { get; set; }
}
The user's contact info could either be in SentTo or SentFrom.
List<ChatMessage> ecml = new List<ChatMessage>();
var q = ecml.OrderByDescending(m => m.SendDate).First();
would give me the latest message, but I need the last message per user.
The closest solution I could find was LINQ Max Date with group by, but I cant seem to figure out the correct syntax. I would rather not create multiple List objects if I don't have to.
If the user's info is in SentTo, my info will be in SentFrom, and vice-versa, so I do have some way of checking where the user's data is.
Did I mention I was very new to LINQ? Any help would be greatly appreciated.
Since you need to interpret each record twice - i.e. as a SentTo and a SentFrom, the query becomes a bit tricky:
var res = ecml
.SelectMany(m => new[] {
new { User = m.SentFrom, m.SendDate }
, new { User = m.SentTo, m.SendDate }
})
.GroupBy(p => p.User)
.Select(g => new {
User = g.Key
, Last = g.OrderByDescending(m => m.SendDate).First()
});
The key trick is in SelectMany, which makes each ChatMessage item into two anonymous items - one that pairs up the SentFrom user with SendDate, and one that pairs up the SentTo user with the same date.
Once you have both records in an enumerable, the rest is straightforward: you group by the user, and then apply the query from your post to each group.
It should be pretty easy, look at this code:
string username = "John";
var q = ecml.Where(i=>i.SentFrom == username || i.SentTo == username).OrderByDescending(m => m.SendDate).First();
It simply filter your collection be choosing items which either SentFrom or SentTo is equal to username.