Retrieving elements from a join with into Entity Framework - c#

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.

Related

Concate Two Member Of List In New Select And Bind To DataGridview Column

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.

Only primitive types do I need to cast?

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.

Convert Many-to-Many relationship to single class with ADO.Net

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

Entity Framework Core Linq query to filter related entity

I've been figuring out a long time on how to write a query on filtering related entity through Entity Framework Core while using Include, suppose I have following two class:
public class Order
{
public int OrderId {get; set;}
public String CreatedBy{get; set;}
public virtual Collection<OrderDetail> OrderDetails { get; set; } = new Collection<OrderDetail>();
}
public class OrderDetail
{
public Int64? OrderDetailID { get; set; }
public Int64? OrderID { get; set; }
public string ProductName { get; set; }
}
if I would like to find all orders created by "Jason" and which order detail has product name equals to "Apple", in sql it would be like:
Hide Copy Code
SELECT *
FROM Orders O
INNER JOIN OrderDetail OD ON O.OrderId = OD.OrderId
WHERE O.CreationUser = 'Jason' and OD.ProductName = 'Apple'
However I am not able to figure out how to write that using EntityFramework, something like below would not work:
await DbContext.Set<Order>()
.Include(p => p.OrderDetails)
.Where(o => o.CreationUser == "Jason")
.Where(o => o.OrderDetails.Where(od => od.ProductName == "Apple"));
There are scenarios like above, I know how to filter property with base entity class like Order in above example but I don't know how to deal with related entity using Include/ThenInclude like filtering on OrderDetail.ProductName, I've been researching a lot but still no clue therefore at the end I have to use Store procedure instead, which is not recommended by most developers.
Maybe a linq sql could do that?
Please help me understand more about it! Thanks very much to everyone who can share your knowledge!
You can simply translate your SQL script to linq:
var orders = (from O in context.Order
join OD in context.OrderDetail on O.OrderId equals OD.OrderId
where O.CreatedBy == "Jason" && OD.ProductName == "Apple"
select order).Distinct().ToList();
//or this solution
orders = context.Set<Order>().Include(p => p.OrderDetails)
.Where(x => x.CreatedBy == "Jason" && x.OrderDetails.Any(y => y.ProductName == "Apple"))
.ToList();
#Slava answer looks correct. But I want to extend his answer. If you want to use like in your query, you can use EF.Functions.Likemethod. It is less expensive in terms of memory and handles complex expressions. You can use the same in your scenario also like the below code. On relational databases, this is usually directly translated to SQL.
var orders = (from O in context.Order
join OD in context.OrderDetail on O.OrderId equals OD.OrderId
where EF.Functions.Like(O.CreatedBy, "Jason") && EF.Functions.Like(OD.ProductName, "Apple")
select order).Distinct().ToList();

Recursive linq results returning duplicates

This question builds off of one I asked last week: Recursive linq to get infinite children. The answer given in that post produced what I needed; a distinct list of Locations and their children based on a parent. We needed to use our own model for Locations, so we created one, and since then, I've been getting duplicate results. Our model is very basic:
class LocationModel
{
public int LocationID { get; set; }
public int ParentLocationID { get; set; }
public string LocationName { get; set;}
}
If you compare it to the entity created by EF, I just cut out all the fields we don't need/use (see link above). So I modified my linq statements to use this new model instead:
DBEntities db = new DBEntities();
public IEnumerable<LocationModel> GetAllChildLocations(int parentId)
{
var locations = (from l in db.Locations
where l.ParentLocationID == parentId ||
l.LocationID == parentId
select new LocationModel()
{
LocationID = l.LocationID,
ParentLocationID = l.ParentLocationID,
LocationName = l.LocationName
}).ToList();
var children = locations.AsEnumerable()
.Union(db.Locations.AsEnumerable()
.Where(x => x.ParentLocationID == parentId)
.SelectMany(y => GetAllChildLocations(y.LocationID)))
.ToList();
return children.OrderBy(l => l.LocationName);
}
When I run it, either in Visual Studio or in LinqPad, I now get duplicates. Here's the original code that does not produce duplicates:
public IEnumerable<Location> GetAllChildLocations(int parentId)
{
var locations = (from l in db.Locations
where l.ParentLocationID == parentId ||
l.LocationID == parentId
select l).ToList();
var child = locations.AsEnumerable()
.Union(db.Locations.AsEnumerable()
.Where(x => x.ParentLocationID == parentId)
.SelectMany(y => GetAllChildLocations(y.LocationID)))
.ToList();
return child;
}
Why is it producing duplicates when I use my own model vs. the generated one from EF? Does it have to do with the auto-generating fields that the EF model has and mine doesn't?
Why is it producing duplicates when I use my own model vs. the generated one from EF?
Because you are using Enumerable.Union method which by default uses reference equality. EF DbContext change tracker keeps internally (tracks) the already loaded entity object instances with the same PK (even if you retrieve them via separate database queries), hence the reference equality works. Which cannot be said for the new LocationModel instances created by the query select operators.
One way to resolve it is to implement GetHashCode and Equals in your LocationModel class. But in general I don't like the implementation of the recursive children retrieval and the usage of Union - there must be a better way, but this is outside the scope of this question (but for the linked).
The root of the evil for me is the following condition
where l.ParentLocationID == parentId || l.LocationID == parentId
which selects both the item and its children, leading to duplicates in the result set, which then are supposed to be eliminated by the Union method. The good implementation will not generate duplicates at all.

Categories