In the image below I have shown the looks of my tables. For the last couple of hours I am Trying to get the best solution for the Query I need but somehow I am running in circles of EF
I would need a collection of Roles with their Modules. Since there are roles which have no modules assigned it should be a left join. the collection should look like " {role, modules}" Role Should be a single object, and modules should be a collection of "Module" objects.
I tried doing it like this
var x = (from r in _context.Role
from rm in r.RoleModule.DefaultIfEmpty()
join m in _context.Module on rm.ModuleID equals m.ID
select new { role= r, modules=rm }).ToList();
EDIT
As per suggestion by lazyberezovsky I made the partial class
public partial class Role
{
public virtual IEnumerable<Module> Modules { get { return RoleModule.Select(p => p.Module); } }
}
Works fine. Another issue of mine is,.... How can I make the "set" accessor.
Edited
Got this now and it works. But How can I access the collection of modules.
var x = (from r in _context.Role
join rm in _context.RoleModule on r.ID equals rm.RoleID into ps
from rm in ps.DefaultIfEmpty()
select new { role=r, modules=rm.Module }).GroupBy(p => p.role).ToList();
* am using this in wpf to be set as datacontext*
Simplest solution will be defining navigation property for modules in your Role entity:
public class Role
{
// ...
public virtual ICollection<Module> Modules { get; set; }
}
Then you will be able to do eager loading of roles with modules:
var roles = _context.Role.Include(r => r.Modules).ToList();
If your junction table is complex, then create navigation property of ICollection<RoleModule> type which holds both Role and Module. Query will look like:
var query = from r in context.Roles
select new {
Role = r,
Modules = r.RoleModules.Select(rm => rm.Module)
};
Instead of starting from Role, how about starting from the join table instead, if you have navigation properties in that class you can include them.
_context.RoleModule()
.Include("Role")
.Include("Module")
.Select(rm=> new {Role = rm.Role, Module = rm.Module}).ToList();
Related
I have a viewmodel that has a header property, and then an associated child collection as such:
public int ProjectApprovalHeaderId { get; set; }
public List<ProjectApprovalStepsVM> ProjectApprovalHistorySteps{ get; set; }
I have a query that returns the data as follows:
ProjectApprovalHeaderId StepName Status
1 Step1 A
1 Step2 C
1 Step3 A
2 Step1 D
I'm trying to write the LINQ so that I can populate the viewmodel with the HeaderId and then the child collection with the actual step data but I'm stuck. I know this isn't much, but it's where I'm at:
var approvalHistory = from s in _context.ProjectApprovalSteps
join u1 in _context.Users on s.AssignedApproverID equals u1.ID
join p in _context.Projects on s.ProjectID equals p.ID
join sc in _context.ApprovalStepStatusCodes on s.Status equals sc.StatusCode into statusList
from scd in statusList.DefaultIfEmpty()
where s.ProjectID == projectId
orderby s.ProjectApprovalHeaderID, s.Sequence
select new ProjectApprovalHistoryVM
{
ProjectApprovalHeaderId = s.ProjectApprovalHeaderID,
ProjectApprovalHistorySteps =
};
vmApprovalHistory.ProjectApprovalHistorySteps = approvalHistory.ToList();
Hoping someone can point me in the right direction.
I'm a fan of Dapper, you can install via Nuget Install-Package Dapper. The reason I recommend, is the multi mapping capabilities. So an object like you have, has nested objects that are needed to be populated and filtered.
public IEnumerable<SpeciesModel> GetAllSpecies() => dbConnection
.Query<SpeciesModel, SpeciesCategoryModel, SpeciesTypeModel, WetlandIndicatorModel, SpeciesModel>(getAllSpeciesQuery,
(species, speciesCategory, speciesType, wetlandIndicator) =>
{
species.SpeciesCategory = speciesCategory;
species.SpeciesType = speciesType;
species.WetlandIndicator = wetlandIndicator;
return species;
});
So the above syntax is expressing the nested object. As your query is returned, it'll also associate your child objects. I believe that is your intent, plus this saves you a lot of time.
More complex example with a collection as a nested object with Dapper.
public IEnumerable<StemModel> GetStemModelForPlotHeader(int projectParameter, int plotParameter,
string plantCommunityParameter) =>
dbConnection.Query<StemModel, PlotSurveyModel, PlantCommunityModel, ProjectModel, SpeciesModel, StemModel>(getAllPlotHeaderDetails,
(stem, survey, plantCommunity, project, species) =>
{
stem.PlotSurvey = survey;
survey.PlantCommunity = plantCommunity;
survey.Project = project;
stem.Species = species;
return stem;
},
new
{
ProjectId = projectParameter,
PlantCommunityCode = plantCommunityParameter,
PlotNumber = plotParameter
});
Update:
Please note the above answer would require you to replace your data access layer. I'm impartial to Dapper, since you receive the performance with the extra mapping functionality. Especially for simple queries, but you can leverage the performance with extra mapping capabilities. Also it forces your data calls to be SQL oriented, not code generating dynamic queries.
I've got three classes User, Order & Project which are stored in single tables. The orders and projects both have a n:n relation with the users.
To implement that I've got two crosstables (UserOrders, UserProjects) which map these relations.
public class User
{
public string UserID {get;set;}
public List<string> Orders{get;set;}
public List<string> Projects {get;set;}
}
public class Order
{
public string OrderID {get;set}
...
}
public class Project
{
public string ProjectID {get;set}
...
}
As you can see the User object contains a list of every related orderID/projectID.
Now I want to query this with Dapper. I' ve got this solution which works pretty fine with one list. But if I try to query the complete user object for the 2nd list I'll get every result multiplied with the number of results in the first list.
So if a user got 3 orders and 2 projects the orderlist will be fine and the projectlist will contain both projects 3 times:
var lookup = new Dictionary<string, User>();
var multi = dbDapperFM.Query<User, string, string, User>("SELECT u.*, uo.OrderID, up.ProjectID "+
"FROM User u INNER JOIN UserOrders uo ON u.UserID=uo.UserID "+
"INNER JOIN UserProjects up ON u.UserID=up.UserID", (u, uo, up) =>
{
User user;
if (!lookup.TryGetValue(m.UserID, out user))
lookup.Add(u.UserID, user= u);
if (user.Orders == null)
user.Orders = new List<string>();
user.Orders.Add(uo);
if (user.Projects == null)
user.Projects = new List<string>();
user.Projects.Add(up);
return user;
}, splitOn: "UserID , OrderID, ProjectID ").AsQueryable();
I understand why this problem occures (2 inner joins), but I don't really get how to solve it.
I also had trouble coming to grips with the fact that Dapper doesn't do this automatically.
First, I'm not sure about comma-separated values for "splitOn." I thought you could only have one value there. So I have multiple columns in my result set named "ID" for example.
Second, to get the proper 1:N relationships you need to do an extra manual step. For example, I did a 2-table join of participants and their phone numbers. Then I had to do this:
private List<Participant> CollapseResultSet(List<Participant> rawdataset)
{
List<Participant> ret = new List<Participant>();
if (!rawdataset.Any())
{
return ret;
}
else
{
List<string> partIds = rawdataset.Select(p => p.ID).Distinct().ToList();
foreach (string pId in partIds)
{
Participant tmp = rawdataset.Where(p => p.ID == pId).FirstOrDefault();
tmp.PhoneNumbers = rawdataset.Where(p => p.ID == pId).Select(n => n.PhoneNumbers[0]).ToList();
ret.Add(tmp);
}
return ret;
}
}
Hope that helps.
I am a little weak in LINQ to SQL so will try to explain my problem.
I have a method as follows (simplified to explain it better):
public static List<ic_ProductData> GetCompleteSimilarProductsWithApplyButton(InfoChoiceAdminDataContext db)
{
var products = (from
p in db.ic_ProductDatas
join proddef in db.ic_ProductDefs on p.ProductDefId equals proddef.ProductDefId
select p
).ToList();
return products;
}
ic_ProductData and ic_ProductDefs are tables in my database
The ic_ProductData class contains a manually created property as:
public ic_ProductDef RelatedProductDef { get; set; }
I want to modify the above LINQ to SQL query so that I can populate this property.
Please note I do not want another call to the database.
Also there are a lot of properties in ic_ProductData so I want to avoid mapping each and every property
Something to the effect of the following (obviously the below is wrong):
public static List<ic_ProductData> GetCompleteSimilarProductsWithApplyButton(InfoChoiceAdminDataContext db)
{
var products = (from
p in db.ic_ProductDatas
join proddef in db.ic_ProductDefs on p.ProductDefId equals proddef.ProductDefId
//trying to change here
select new ic_ProductData
{
//do something with p here so that all the properties of new object gets filled
// avoid mapping of properties here
RelatedProductDef = proddef
}
).ToList();
return products;
}
With my limited knowledge I am stuck here.
Please help!
Thanks in advance!
You can do something like this:
var query = (from p in db.ic_ProductDatas
join proddef in db.ic_ProductDefs on p.ProductDefId equals proddef.ProductDefId
select new
{
ProductData = p,
Def = proddef
}).ToList();
List<ic_ProductData> products = new List<ic_ProductData>();
foreach( var product in query)
{
product.ProductData.RelatedProductDef = product.Def;
products.Add(product);
}
Basicly, you first need to do the one query to the database, this returns an anonymous type containing both your product and its Def.
Finally, you loop (in memory, no db-calls!) over these, creating your final objects with their RelatedProductDef properties populated.
In entity framework i have two table (two entities): People and Role with one to many relationship.
In the People tables i have a navigation property to Role:
//People.cs
public virtual ICollection<Role> Role { get; set; }
Now i want retrieve all people that have role as 'barman'. How can i achieve this?
I want use linq to entities in the query expression method. I've tried:
var listPerson = (from p in SiContext.People
where p.Role.Name = 'barman'
select p).ToList();
The problem is that i cannot make p.Ruolo.Name because p.Ruolo is a ICollectionType that doesn't have the property "Name" (while the entity Role has that property)
Since role is a collection, you need to use Any
var listPerson = (from p in SiContext.People
where p.Role.Any(x => x.Name == "barman")
select p).ToList();
Just to complemente your code include ToLower():
var listPerson = (from p in SiContext.People
where p.Role.Any(x => x.Name.ToLower() == "barman")
select p).ToList();
You can try the order way around (assuming you have the reverse navigation property)
var listPerson = SiContext.Role.First(r => r.Name == "barman").People.ToList();
Maybe I'm going about this the wrong way...
I have an Order table and an OrderItem table. I create a new Order using linq2sql generated classes.
I then attempt to get all orderable items out of my database using a query that goes after various tables.
I try to then create a new list of OrderItem from that query, but it squawks that I can't explicitly create the object.
Explicit construction of entity type OrderItem in query is not allowed.
Here is the query:
return (from im in dc.MasterItems
join c in dc.Categories
on im.CATEGORY equals c.CATEGORY1
select new OrderItem()
{
OrderItemId = im.ItemId
});
The idea is to populate the database with all orderable items when a new order is created, and then display them in a grid for updates. I'm taking the results of that query and attempting to use AddRange on Order.OrderItems
Is there a proper strategy for accomplishing this using linq2sql?
Thanks in advance for your help.
From my understanding of L2S, I don't think you can use explicit construction (in other words new SomeObj() { ... }) in a query because you aren't enumerating the results yet. In other words, the query has just been built, so how are you supposed to do this:
SELECT new OrderItem() FROM MasterItems im JOIN Categories c on c.CATEGORY1 = im.CATEGORY
This is what you're trying to do, which doesn't work because you can't return a POCO (unless you join the OrderItem back somehow and do OrderItem.* somewhere). Ultimately, what you would have to do is just enumerate the collection (either in a foreach loop or by calling ToList()) on the query first and then build your OrderItem objects.
var query = (from im in dc.MasterItems
join c in dc.Categories
on im.CATEGORY equals c.CATEGORY1
select new { MasterItem = im, Category = c});
List<OrderItem> returnItems = new List<OrderItem>();
foreach(var item in query)
{
returnItems.Add(new OrderItem() { OrderItemId = item.MasterItem.ItemId });
}
return returnItems;
OR
return (from im in dc.MasterItems
join c in dc.Categories
on im.CATEGORY equals c.CATEGORY1
select new { MasterItem = im, Category = c})
.ToList()
.Select(tr => new OrderItem() { OrderItemId = tr.MasterItem.ItemId });
Try that out and let me know if that helps.
Expand the order class by creating a partial file where that class OrderItem now has property(ies) which lend itself to business logic needs, but don't need to be saved off to the database.
public partial class OrderItem
{
public int JoinedOrderItemId { get; set; }
public bool HasBeenProcessed { get; set; }
}