if control in linq? - c#

This query is what I got:
var hede = (from customer in _customerRepository.Table
join source in _sourcedefinitionepository.Table on customer.SourceCode equals source.SourceCode
select new {Customer = customer, source.SourceName}
And then I wrote this:
if (agencyName ! = null )
hede = hede.Where(p => p.Customer.Name.StartsWith(agencyName));
How can i put the if code into first part of code?

You can achieve it in this way
where agencyName == null || customer.Name.StartsWith(agencyName));
Full query
var hede = (from customer in _customerRepository.Table
join source in _sourcedefinitionepository.Table on customer.SourceCode equals source.SourceCode
where agencyName == null || customer.Name.StartsWith(agencyName))
select new {Customer = customer, source.SourceName}
Updated
Using lamda.
var hede = _customerRepository.Table.Join(_sourcedefinitionepository.Table, c => c.SouceCode , s => s.SourceCode,
(c, s) => new
{
Customer = c,
s.SourceName
})).Where(p => agencyName == null || p.Customer.Name.StartsWith(agencyName)).ToList();

You can use the following code
var hede = _customerRepository.Table.Join(_sourcedefinitionepository.Table,
x => x.SourceCode,
y => y.SourceCode,
(customer, source) => new { customer, source.SourceName})
.Where(p => agencyName == null ||
(p.customer.Name.Any(f => p.customer.Name.StartsWith(agencyName))));
lambda code
var hede =from customer in _customerRepository.Table
join source in _sourcedefinitionepository.Table
on customer.SourceCode equals source.SourceCode
where agencyName == null || customer.Name.Any(f => customer.Name.StartsWith(agencyName))
select new { Customer = customer, source.SourceName };

Related

Why Same where clause need to write multiple times in a Linq Query for following SQL

What will be the proper linq syntax of below SQL Query ?
select a.id, a.AppointmentStatusID, ad.ID as DetailID
from [dbo].[Appointment] a, [dbo].[AppointmentDetail] ad
where a.[ID] = ad.[AppointmentID]
and a.CompanyID = 'a3dea87a-804e-4115-98cf-472988cf1678'
and a.LocationID = '3165caca-2a48-46f0-bbed-578cff29167t'
and ad.AppDateFrom <= {ts '2017-11-14 23:59:31'}
and ad.AppDateTo >= {ts '2017-11-14 00:00:00'}
and ad.[ApprovalStatusID] = 2
Problem I faced:
I required to filter Where Condition two times 1st at the time within the join & 2nd time during the object.Select expression, please check bellow
var results = (from a in appointments
join ad in _appointmentDetailRepository.GetAll() on a.ID equals ad.AppointmentID
where ad.ApprovalStatusID == 2
&& DbFunctions.TruncateTime(ad.AppDateFrom) <= DbFunctions.TruncateTime(viewmodel.AppointmentDate)
&& DbFunctions.TruncateTime(ad.AppDateTo) >= DbFunctions.TruncateTime(viewmodel.AppointmentDate)
orderby a.ID
select new Appointment
{
ID = a.ID,
CompanyID = a.CompanyID,
LocationID = a.LocationID,
AppointmentDetail = a.AppointmentDetail.Select(ad => new AppointmentDetail
{
ID = ad.ID,
AppDateFrom = ad.AppDateFrom,
AppDateTo = ad.AppDateTo,
AppointmentStatusID = ad.AppointmentStatusID,
}).Where(ad=> ad.ApprovalStatusID == 2
&& DbFunctions.TruncateTime(ad.AppDateFrom) <= DbFunctions.TruncateTime(viewmodel.AppointmentDate)
&& DbFunctions.TruncateTime(ad.AppDateTo) >= DbFunctions.TruncateTime(viewmodel.AppointmentDate)).ToList()
}).GroupBy(x => x.ID).Select(x => x.DefaultIfEmpty().FirstOrDefault());
Query : Why I required to write Where clause 2 times ?
Required Result
An Appointment Object --> Containing ICollection<AppoinmentDetails> if Details.Where Condition == True
From what I see (without knowing the model you have), it looks like you should use the already joined and filtered Details from ad instead of looking it up again from the Property a.AppointmentDetail...
Untested:
select new Appointment
{
ID = a.ID,
CompanyID = a.CompanyID,
LocationID = a.LocationID,
AppointmentDetail = ad.ToList(), // <-- don't you think?
...
}
For the given SQL query
select a.id, a.AppointmentStatusID, ad.ID as DetailID
from [dbo].[Appointment] a, [dbo].[AppointmentDetail] ad
where a.[ID] = ad.[AppointmentID]
and a.CompanyID = 'a3dea87a-804e-4115-98cf-472988cf1678'
and a.LocationID = '3165caca-2a48-46f0-bbed-578cff29167t'
and ad.AppDateFrom <= {ts '2017-11-14 23:59:31'}
and ad.AppDateTo >= {ts '2017-11-14 00:00:00'}
and ad.[ApprovalStatusID] = 2
LINQ query can be written as
var results = (from a in appointments
join ad in appointmentDetails on a.ID equals ad.AppointmentID
where ad.ApprovalStatusID == 2
&& a.CompanyID == "a3dea87a-804e-4115-98cf-472988cf1678"
&& a.LocationID == "3165caca-2a48-46f0-bbed-578cff29167t"
&& ad.AppDateFrom.Date <= viewmodel.AppointmentDate.Date
&& ad.AppDateTo.Date >= viewmodel.AppointmentDate.Date
select new
{
ID = a.ID,
AppointmentStatusID = a.AppointmentStatusID,
DetailID = ad.ID
}).ToList();
You can also write it like
var results = appointmentDetails
.Where(ad => ad.AppDateFrom.Date <= viewmodel.AppointmentDate.Date
&& ad.AppDateTo.Date >= viewmodel.AppointmentDate.Date
&& ad.ApprovalStatusID == 2
&& ad.Appointment.CompanyID == "a3dea87a-804e-4115-98cf-472988cf1678"
&& ad.Appointment.LocationID == "3165caca-2a48-46f0-bbed-578cff29167t")
.Select(ad =>
new
{
ID = ad.Appointment.ID,
AppointmentStatusID = ad.Appointment.AppointmentStatusID,
DetailID = ad.ID
})
.ToList()
As per the updated question, to get the the Appointment object with a collection of AppointmentDetails, please try this query
var results = appointmentDetails
.Where(ad => ad.AppDateFrom.Date <= viewmodel.AppointmentDate.Date
&& ad.AppDateTo.Date >= viewmodel.AppointmentDate.Date
&& ad.ApprovalStatusID == 2
&& ad.Appointment.CompanyID == "a3dea87a-804e-4115-98cf-472988cf1678"
&& ad.Appointment.LocationID == "3165caca-2a48-46f0-bbed-578cff29167t")
.Select(ad =>
new
{
ID = ad.Appointment.ID,
AppointmentStatusID = ad.Appointment.AppointmentStatusID,
Detail = ad
})
.AsEnumerable()
.GroupBy(a => new { a.ID, a.AppointmentStatusID })
.Select(a => new Appointment
{
ID = a.Key.ID,
AppointmentStatusID = a.Key.AppointmentStatusID,
AppointmentDetails = a.Select(d => d.Detail).ToList()
})
.ToList();

Yet another “A query body must end with a select clause or a group clause”

This query does work, but I am trying to combine the two steps into one query.
var query1 = from b in db.GetTable<Boats>()
from o in db.GetTable<Offices>()
from u in db.GetTable<Users>()
.Where
(u =>
u.UserId == b.Handling_broker &&
o.Office == b.Handling_office &&
b.Status == 2 &&
officesToInclude.Contains(b.Handling_office)
)
select new
{
hOffice = o.Name,
bName = u.Name
};
var query2 = query1.GroupBy(t => new { office = t.hOffice, name = t.bName })
.Select(g => new { Office = g.Key.office, Name = g.Key.name, Count = g.Count() });
If I try to combine the two queries using the following query it gives me the “A query body must end with a select clause or a group clause” error.
var query1 = from b in db.GetTable<Boats>()
from o in db.GetTable<Offices>()
from u in db.GetTable<Users>()
.Where
(u =>
u.UserId == b.Handling_broker &&
o.Office == b.Handling_office &&
b.Status == 2 &&
officesToInclude.Contains(b.Handling_office)
)
.GroupBy(t => new { office = t.Office, name = t.Name })
.Select(g => new { Office = g.Key.office, Name = g.Key.name, Count = g.Count() });
I think I have to add a select something, but I can't figure out what.
Can anyone please help?
Your query must contain a select clause. The .Where(...).GroupBy(...).Select(...) are only on the db.GetTable<Users>(). Something like:
var query1 = from b in db.GetTable<Boats>()
from o in db.GetTable<Offices>()
from u in db.GetTable<Users>().Where(u => u.UserId == b.Handling_broker &&
o.Office == b.Handling_office &&
b.Status == 2 &&
officesToInclude.Contains(b.Handling_office))
.GroupBy(t => new { office = t.Office, name = t.Name })
.Select(g => new { Office = g.Key.office, Name = g.Key.name, Count = g.Count() })
select new { /* Desired properties */};
But I think you are looking for something like:
var result = from b in db.GetTable<Boats>()
from o in db.GetTable<Offices>()
from u in db.GetTable<Users>()
where u.UserId == b.Handling_broker &&
o.Office == b.Handling_office &&
b.Status == 2 &&
officesToInclude.Contains(b.Handling_office))
group 1 by new { t.Office, t.Name } into g
select new { Office = g.Key.Office, Name = g.Key.Name, Count = g.Count() };

Query Users who have all requested records in two tables

Data structure looks like:
User(id)
UserApp(user_id, app_id)
UserSkill(user_id, skill_id)
Using linq-to-sql or EF, how would I construct a query to elegantly return only users who possess every requested app and skill?
In addition, how would I adjust the query to return any user who possesses at least one of the requested apps or skills? Essentially an OR vs AND (above).
UPDATE 1:
So I think we're close. Basically I want to only return users who have ALL the requested apps and skills. If we have two arrays of requested ids for skills and apps:
int[] requestedAppIDs // [1, 2, 3]
int[] requestedSkillIDs // [4, 5, 6]
I would only want to return a user if they have apps 1,2,3 AND skills 4,5,6.
var usersWithAllSelectedAppsAndSkills =
context.Users
.GroupJoin(context.UserApp,
k => k.id,
k => k.user_id,
(o, i) => new { User = o, UserApps = i })
.GroupJoin(context.UserSkill,
k => k.User.id,
k => k.user_id,
(o, i) => new { User = o.User, o.UserApps, UserSkills = i })
.Where(w => !requestedAppIDs.Except(w.UserApps.Select(x => x.app_id).ToArray()).Any() && !requestedSkillIDs.Except(w.UserSkills.Select(x => x.skill_id).ToArray()).Any())
.Select(s => s.User)
.ToList();
Obviously, LINQ does not know how to translate the UserSkills.Select().ToArray()'s in my Where() to SQL. How can I accomplish this?
And, secondarily the OR solution as well (user has any one of the requested apps or skills).
This will do the job as long as the user_id – app_id and user_id – skill_id values in the UserApp and UserSkill tables are unique.
var requestedSkillIDs = new[] { 4, 5, 6 };
var skillCount = requestedSkillIDs.Length;
var requestedAppIDs = new[] { 1, 2, 3 };
var appCount = requestedAppIDs.Length;
using (var context = new TestContext()) {
context.Database.CreateIfNotExists();
var appQuery = context.UserApp.Where(p => requestedAppIDs.Contains(p.AppId))
.GroupBy(p => p.UserId)
.Where(p => p.Count() == appCount);
var skillQuery = context.UserSkill.Where(p => requestedSkillIDs.Contains(p.SkillId))
.GroupBy(p => p.UserId)
.Where(p => p.Count() == skillCount);
var result = from a in appQuery
join s in skillQuery on a.Key equals s.Key
join u in context.Users on s.Key equals u.Id
select u;
var users = result.ToList();
}
Here's one way to do it, I hope i got all the syntax right :)
using (var context = new YourContext())
{
var usersWithAllSkills = context.User
.Where(w => w.id == yourId)
.Join(context.UserApp,
k => k.id,
k => k.user_id,
(o,i) => o)
.Join(context.UserSkill,
k => k.id,
k => k.user_id,
(o,i) => o)
.ToList();
var usersWithAnySkill = context.User
.Where(w => w.id == yourId)
.GroupJoin(context.UserSkill,
k => k.id,
k => k.user_id,
(o,i) => new { User = o, UserSkills = i })
.GroupJoin(context.UserApp,
k => k.User.id,
k => k.user_id,
(o,i) => new { User = o.User, o.UserSkills ,UserApps = i })
.Where(w => w.UserSkills != null || w.UserApps != null)
.Select(s => s.User)
.ToList();
}
For the first case (AND) You just need to make inner join like below:
from t1 in db.UserApp
join t2 in db.UserSkill on t1.user_id equals t2.user_id
where t1.app_id == "someID" && t2.skill_id == "someID"
select new { t1.user_id,t1.user_app_id, t2.user_skill}
For the second case just swap &&(AND) with ||(OR).
There is a more direct way to write the required queries using L2E. To write these queries you have to forget thinking in SQL and start thinking in LINQ.
For the first case, look for users which have all the skills:
var usersWithAll = ctx.Users2.Where(u =>
appIds.All(aid => u.Apps.Any(a => a.AppId == aid))
&& skillIds.All(sid => u.Skills.Any(s => s.SkillId == sid))
);
Translated as: get the users where, for all the appIds the user has al leat an app with that application id and for all skillIds the user has at least one skill with that id
And, for the second case, users which have any of the apps and any of the skills:
var usersWithAny = ctx.Users2.Where(u =>
appIds.Any(aid => u.Apps.Any(a => a.AppId == aid))
&& skillIds.Any(sid => u.Skills.Any(s => s.SkillId == sid))
).ToList();
Translated as: get the users where, for at least one appId the user has an app with that application id and for any skillIds the user has at least one skill with that id
If you run this test class, you'll also see the executed query (please, note that, to do so, I'm using the Log property of Database. I think it's only available from EF6 on).
namespace Tests
{
[TestClass]
public class CheckSeveralRelationsAtOnce
{
[TestMethod]
public void HasAllAppsAndSkills()
{
int[] appIds = {1, 2, 3};
int[] skillIds = {6, 7, 8};
using (var ctx = new MyDbContext())
{
ctx.Database.Log = Console.Write;
var usersWithAll = ctx.Users2.Where(u =>
appIds.All(aid => u.Apps.Any(a => a.AppId == aid))
&& skillIds.All(sid => u.Skills.Any(s => s.SkillId == sid))
).ToList();
Assert.IsNotNull(usersWithAll);
}
}
[TestMethod]
public void HasAnyAppsOrSkill()
{
int[] appIds = { 1, 2, 3 };
int[] skillIds = { 6, 7, 8 };
using (var ctx = new MyDbContext())
{
ctx.Database.Log = Console.Write;
var usersWithAny = ctx.Users2.Where(u =>
appIds.Any(aid => u.Apps.Any(a => a.AppId == aid))
&& skillIds.Any(sid => u.Skills.Any(s => s.SkillId == sid))
).ToList();
Assert.IsNotNull(usersWithAny);
}
}
}
}
I believe the answer by codeworx is correct for getting users with all the skills / apps
As an aside - I answered pretty much the same question recently with a pure SQL solution (SQL Server) - that could be turned into a stored procedure (with a table valued parameter) - See here if interested. This will perform better for a large number of values. Entity framework will turn every skill/app in the list into its own SQL parameter, which is much slower.
Unfortunately Entity framework doesn't really support table valued parameters yet - although you can use the entity framework connection to directly call a stored procedure with a table valued parameter (as per this article
Back to the question at hand...
I'll add the (easier) query to select a user with ANY of the skills & ANY of the apps:
var result = from u in context.Users
join _a in (
from a in context.UserApp
where requestedAppIDs.Contains(a.AppId)
select a.UserId;
) on u.Id equals _a
into aGrp
join _s in (
from s in context.UserSkill
where requestedSkillIDs.Contains(s.SkillId)
select s.UserId;
) on u.Id equals _s
into sGrp
where aGrp.Any()
&& sGrp.Any()
select u;
And just for completeness - the ALL solution again:
var skillCount = requestedSkillIDs.Length;
var appCount = requestedAppIDs.Length;
var result = from u in context.Users
join _a in (
from a in context.UserApp
where requestedAppIDs.Contains(a.AppId)
select a.UserId;
) on u.Id equals _a
into aGrp
join _s in (
from s in context.UserSkill
where requestedSkillIDs.Contains(s.SkillId)
select s.UserId;
) on u.Id equals _s
into sGrp
where aGrp.Count() == appCount
&& sGrp.Count() == skillCount
select u;
and finally - an example where the main query body is fixed, but you can add differing where clauses depending upon the AND/OR requirement
bool onlyReturnWhereAllAreMatched = false;
var skillCount = requestedSkillIDs.Length;
var appCount = requestedAppIDs.Length;
IQueryable<User> result;
var query = from u in context.Users
join _a in (
from a in context.UserApp
where requestedAppIDs.Contains(a.AppId)
select a.UserId;
) on u.Id equals _a
into aGrp
join _s in (
from s in context.UserSkill
where requestedSkillIDs.Contains(s.SkillId)
select s.UserId;
) on u.Id equals _s
into sGrp
select new {u, aCount = aGrp.Count(), sCount = sGrp.Count()};
if (onlyReturnWhereAllAreMatched)
{
result = from x in query
where x.aCount == appCount
&& x.sCount == skillCount
select x.u;
} else {
result = from x in query
where x.aCount > 0
&& x.sCount > 0
select x.u;
}

LINq query throws exception

I am running the following query
var allroles = from l in metaData.Role select l.RoleId;
var personroles = from k in metaData.PersonRole
where k.PersonId == new Guid(Session["user_id"].ToString())
select k.RoleId;
Dictionary<Guid, string> allroleswithnames =
(from l in metaData.Role
select new { l.RoleId, l.Description })
.ToDictionary(u => u.RoleId, u => u.Description);
var avl_roles = from j in allroles.Except(personroles)
select new
{
RoleId = j,
Description = allroleswithnames[new Guid(j.ToString())]
};
clist_avl_roles.DataSource = avl_roles;
clist_avl_roles.DataBind();
The code at code for avl_roles throwing error
Subquery returned more than 1 value. This is not permitted when the subquery
follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Actually there are multiple rows for roleid with same person id. How do I rewrite the query to handle this situation?
var personId = new Guid(Session["user_id"].ToString());
var personRoles = metaData.PersonRole
.Where(pr => pr.PersonId == personId)
.Select(pr => pr.RoleId);
var avl_roles = from r in metaData.Role
where !personRoles.Contains(r.RoleId)
select new { r.RoleId, r.Description };
Or in single query
var avl_roles = from r in metaData.Role
join pr in metaData.PersonRole.Where(x => x.PersonId == personId)
on r.RoleId equals pr.RoleId into g
where !g.Any()
select new { r.RoleId, r.Description };

Unknown column error using Entity Framework and LINQ

I started with EF yesterday and I am in trouble to transform this simple query into EF sintax
Translate:
select a.city from offer o, address a, offer_address oa
where o.identifier = oa.offeridentifier
and a.identifier = oa.addressidentifier
group by a.city
order by count(*) desc
Into:
var cities = (from o in db.offer
from a in db.address
from oa in db.offer_address
where (o.identifier == oa.offeridentifier
&& a.identifier == oa.addressidentifier)
group a by a.city into c
select new
{
quantity = c.Count(),
city = c.Key
}).OrderByDescending(a => a.quantity).Select(a => a.city);
var cityCollection = new List<string>();
foreach (var city in cities)
cityCollection.Add(city.ToString());
I have tried withou success
var cities = (from oa in db.offer_address
from of in db.offer.Where(x => x.identifier == oa.identifier)
from ad in db.address.Where(y => y.identifier == oa.offeridentifier).AsEnumerable()
group ad.city by new { ad.city } into g
select new
{
quantity = g.Count(),
city = g.Key
}).OrderByDescending(a => a.quantity);
The problem occurs when try to get inside the first loop!
Unknown column 'GroupBy1.K1' in 'field list'`
Line 55: foreach (var city in cities)`
With the second case:
Can't group on 'A1'
UPDATE
This code works, but its not i need
var cities = (
from of in db.offer
from ad in db.address
from oa in db.offer_address
where (of.identifier == oa.offeridentifier && ad.identifier == oa.addressidentifier)
group ad.city by new { ad.city } into g
select new
{
quantity = g.Count()
}).OrderByDescending(a => a.quantity)
Or
var cities = (
from of in db.offer
from ad in db.address
from oa in db.offer_address
where (of.identifier == oa.offeridentifier && ad.identifier == oa.addressidentifier)
group ad.city by new { ad.city } into g
select new
{
city = g.Key
});
Update - Try adding AsEnumerable() after the first select
var query = (from o in db.offer
from a in db.address
from oa in db.offer_address
where (o.identifier == oa.offeridentifier && a.identifier == oa.addressidentifier)
group a by a.city into c
select new
{
quantity = c.Count(),
city = c.Key
})
.AsEnumerable()
.OrderByDescending(a => a.quantity)
.Select(a => a.city);

Categories