Left Join using LAMBDA to get Result in API - asp.net-web-api

How to implement this Join which is in the code below into C# using LAMBDA
Select
VD.Id
, VD.BusinessAddress
, VD.BusinessDesc
, VD.BusinessEmail
, VD.BusinessName
, VD.BusinessZip
, VD.ContactPerson
, VD.ContactNo
, VD.ProfileUrl
, L.Name
, BC.BusinessCategory
from vendorDomain VD WITH(NOLOCK)
left Join Location L WITH(NOLOCK) ON VD.City = L.Id
left join Business_Category BC WITH(NOLOCK) ON VD.BusinessCategory = BC.BusinessId
where VD.IsDeleted = 0
I have to implement the join operation in the following API:
[HttpGet]
public async Task<IActionResult> Get()
{
var VendorList =await _vendorRepository.Query().Where(x => x.IsDeleted == false).ToListAsync();
return Ok(VendorList);
}
There are alot of examples out there but are way to confusing for a novice developer..
EDIT:
This is what I have tried as of now:
var employees = from vndr in context.vendorDomain
join C in context.Location on vndr.City equals C.Id into dep
from dept in dep.DefaultIfEmpty()
select new
{
vndr.BusinessAddress,
vndr.BusinessDesc,
vndr.BusinessEmail,
vndr.BusinessName,
vndr.BusinessWebsite,
vndr.BusinessZip,
vndr.ContactNo,
vndr.ContactPerson,
vndr.Created_At,
vndr.ProfileUrl,
vndr.Url,
dept.Name
};

We will do things first: do the joins and create a view model class that you will return. Because returning anonymous object and using dynamic does get messy.
ViewModel for the joined entities:
public class EmployeesViewModel
{
public string BusinessAddress { get; set; }
public string BusinessDesc { get; set; }
public string BusinessEmail { get; set; }
/* ....all remaining properties */
}
Then we join them properly and select them as an EmployeeViewModel:
var employees = from vndr in context.vendorDomain
join loc in context.Location on vndr.City equals loc.Id
join bus in context.Business_Category on vndr.BusinessCategory = bus.BusinessId
select new EmployeeViewModel
{
BusinessAddress = vndr.BusinessAddress,
BusinessDesc = vndr.BusinessDesc,
BusinessEmail = vndr.BusinessEmail,
/* ... remaining properties here*/
};
Or, if you want the method syntax:
var employees = context.vendorDomain
.Join(context.Location,
vndr => vndr.City,
loc => loc.Id,
(vndr, loc) => new { vndr, loc,})
.Join(context.Business_Category,
vndr_loc.vndr.BusinessCategory,
bus.BusinessId,
(vndr_loc, bus) => new {vndr_loc.vndr, vndr_loc.loc, bus})
.Select(x => new EmployeeViewModel{
BusinessAddress = vndr.BusinessAddress,
BusinessDesc = vndr.BusinessDesc,
BusinessEmail = vndr.BusinessEmail,
/* ... remaining properties here*/
});
As per your comment, you need to print the vendorList after the join. Now that is pretty vague, but I assume you want to submit both to your client / view, so again, we create a ViewModel class for it:
public class EmployeeVendorListViewModel
{
public VendorList VendorList { get; set; }
public EmployeeViewModel Employees { get; set; }
}
The last thing we do is glue it all together in your ActionMethod and return it:
[HttpGet]
public async Task<IActionResult> Get()
{
//renamed using a lower case "v"
var vendorList = await _vendorRepository.Query()
.Where(x => x.IsDeleted == false)
.ToListAsync();
//the join from earlier. You should put it in a repo somewhere, so it does not clutter your controller
var employees = from vndr in context.vendorDomain
join loc in context.Location on vndr.City equals loc.Id
join bus in context.Business_Category on vndr.BusinessCategory = bus.BusinessId
select new EmployeeViewModel
{
BusinessAddress = vndr.BusinessAddress,
BusinessDesc = vndr.BusinessDesc,
BusinessEmail = vndr.BusinessEmail,
/* ... remaining properties here*/
};
//create the final view model and return it
var vm = new EmployeeVendorListViewModel
{
VendorList = vendorList,
Employees = employees
}
return Ok(vm);
}
If you want to use NOLOCK in your query, you have to wrap it in a TransactionScope. This has already been answered here on StackOverflow: NOLOCK with Linq to SQL

Related

Extending a Select.... to accommodate more fields as extension using EF

I have a class:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Salary { get; set; }
public string Address {get;set;}
}
And query using Entity Framework is:
var selectedEmployee = entities.Employees
.Where(e=>e.Salary>10000)
.Select(emp => new EmpDTO
{
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary
});
My question is:
I want to allow extending this query without rewriting the base query. It should allow adding a new field in the .Select(.....) by extending the above query.
Without rewriting the complete query:
var selectedEmployee = entities.Employees
.Where(e=>e.Salary>10000)
.Select(emp => new EmpDTO
{
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
Address = emp.Address
});
How can I do that?
Thanks
If I understand, you can try this:
public IQuerable<EmpDTO> GetEmployee(Func<Employee, EmpDTO> projection = null)
{
if(projection == null)
projection = emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
};
return entities.Employees.Where(e => e.Salary > 10000).Select(projection);
}
Implementation:
var query = classInstance.GetEmployee();
//or
query = classInstance.GetEmployee(emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
Address = emp.Address
});
If you always want to get some set of fields, like Id, Name and
Salary and sometimes take additional fields(and specify only their
as method arguments), you should to take all fields from DB and only
then filter them depends on your condition - it is bad practice to do
SELECT *, so you should get default set of fields or specify all desired fields mannualy.
Solution with SELECT *:
public List<EmpDTO> GetEmployee(Func<Employee, EmpDTO> projection)
{
var query = entities.Employees.Where(e => e.Salary > 10000).ToList().Select(x => {
var item = projection == null ? new EmpDTO() : projection(x);
item.Id = x.Id;
item.Name = x.Name;
item.Salary = x.Salary;
return item;
}).ToList();
}
At this case return value is List<T> not IQuerable<T>;
Implementation:
var items = classInstance.GetEmployee(emp => new EmpDTO { Address = emp.Address });
//items also will contain fields: Id, Name and Salary by default

Full outer join linq using union

I have two lists
var left={[ID=1,Name='A',Qty1=0,Qty2=5],[ID=2,Name=B,Qty1=0,Qty2=52]};
var right={[ID=1,Name='A',Qty1=57,Qty2=0],[ID=2,Name=B,Qty1=84,Qty2=0]};
var outer=left.union(right);
I want to get the following result:
outer={[ID=1,Name='A',Qty1=57,Qty2=5],[ID=2,Name=B,Qty1=84,Qty2=52]}
How do I get that? How to write the comparator class?
Edit:
I have two lists
var target=(...new ClassA{ID=a.ID,Name=a.Name,TargetQty=b.TargetValue}).ToList();
var sales=(....new ClassA{ID=a.ID,Name=a.Name,SalesQty=b.SaleValue}).ToList();
Now I want a full outer join. How can I get that?
It sounds like you possibly want an inner join:
var query = left.Join(right, l => l.Id, r => r.Id,
(l, r) => new { l.Id, l.Name, r.Qty1, l.Qty2 });
(You may want to join on both Id and Name; it's not clear whether the Id is enough.)
In this case union will not bring the desired output.
Jon Skeet is right in his direction that inner join will do what you want. Considering your question it seems like you need to have inner join with sum of respective row like below. If it is not your requirement then you need to modify your question.
public class Program
{
public static void Main()
{
var left= new List<Product>(){
new Product(){ID=1,Name="A",Qty1=0,Qty2=5},
new Product(){ID=2,Name="B",Qty1=0,Qty2=52}
};
var right= new List<Product>(){
new Product(){ID=1,Name="A",Qty1=57,Qty2=0},
new Product(){ID=2,Name="B",Qty1=84,Qty2=0}
};
var outer=left.Union(right);
Console.WriteLine(outer.Count());//will be four which is not expected.
var query = left.Join(right, d => d.ID, e=> e.ID, (f,g) => new Product(){ID = f.ID, Name = f.Name, Qty1 = f.Qty1+g.Qty1, Qty2 = f.Qty2+g.Qty2});
foreach(var item in query)
{
item.Print();
}
}
}
public class Product {
public int ID{get; set;}
public string Name{get; set;}
public int Qty1{get; set;}
public int Qty2{get; set;}
public void Print()
{
Console.WriteLine("ID : {0}", ID);
Console.WriteLine("Name: {0}", Name);
Console.WriteLine("Qty1: {0}", Qty1);
Console.WriteLine("Qty2: {0}", Qty2);
}
}
Here is output from above code:
4
ID : 1
Name: A
Qty1: 57
Qty2: 5
ID : 2
Name: B
Qty1: 84
Qty2: 52
You may modify and test this code here => link

Linq to Entities using joins and generic Lists

I'm using Linq and just started working with it.
My gridview was populating when I was using a single table, but now I am trying to join a Client First Name from the Client Info table.
Error is Event_Setup does not contain a definition for ClientFirstName. So it is checking Event_Setup table instead of Client_Info.
public List<EventData> GetDetails()
{
using (EMSEntities db = new EMSEntities())
{
var context = from events in db.Event_Setup
join clients in db.Client_Info on events.ClientId equals clients.ClientId
select events;
List<EventData> newEvent = new List<EventData>();
foreach (var e in context)
{
EventData test = new EventData();
test.Event_Title = e.EventTitle;
//Error on e.ClientFirstName, Event Setup does not contain Definition
(located in Client_Info table not Event Setup)
test.Name = e.ClientFirstName;
test.Start_Date = e.EventDateFrom;
test.End_Date = e.EventDateFrom;
newEvent.Add(test);
}
return newEvent;
}
}
DAL
public class EventData
{
public string Event_Title { get; set; }
public string Name { get; set; }
public DateTime? Start_Date { get; set; }
public DateTime? End_Date { get; set; }
}
This code is not doing anything with the joined clients data. You can see it is just selecting events:
var context = from events in db.Event_Setup
join clients in db.Client_Info on events.ClientId equals clients.ClientId
select events;
Once you get out of that LINQ statement, clients is gone. It doesn't merge any data for you. You need to do a projection, like this:
var context = from events in db.Event_Setup
join clients in db.Client_Info on events.ClientId equals clients.ClientId
select new EventData
{
Event_Title = events.EventTitle,
Name = clients.ClientFirstName,
Start_Date = events.EventDateFrom,
End_Date = events.EventDateFrom
};
Thank you for that Cory, here's what the list would like for anyone who views this questions
foreach (var e in context)
{
EventData test = new EventData();
test.Event_Title = e.Event_Title;
test.Name = e.Name;
test.Start_Date = e.Start_Date;
test.End_Date = e.End_Date;
newEvent.Add(test);
}
return newEvent;

select all related objects

I have a linq query where I'm trying to return all MlaArticles that are related to all other WebObjects but I'm getting the error: The specified type member 'RelatedWebObjectIds' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Here's the Model...
public abstract class WebObject : IValidatableObject
{
public WebObject()
{
this.Id = Guid.NewGuid();
RelatedTags = new List<Tag>();
RelatedWebObjects = new List<WebObject>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
public virtual ICollection<WebObject> RelatedWebObjects { get; set; }
public IList<Guid> RelatedWebObjectIds { get; set; }
}
Thanks for your help...
List<MlaArticle> assignedWebObjects = (from e in db.MlaArticles
where
(from w in db.WebObjects
from r in w.RelatedWebObjectIds
where w.Id == id
select r).Contains(e.Id)
select e).OrderBy(x => x.Title).ToList();
New query. Produces different error: WebObject does not contain a definition for 'Contains' and the best extension method overload ... has some invalid arguments.
List<MlaArticle> assignedWebObjects = (from e in db.MlaArticles
where
(from w in db.WebObjects
from r in w.RelatedWebObjects
where w.Id == id
select r.RelatedWebObjectIds).Contains(e.Id)
select e).OrderBy(x => x.Title).ToList();
Is there a RelatedWebObjects navigation property in your MlaArticle entity? If there is, you can do that instead:
List<MlaArticle> assignedWebObjects = (from e in db.MlaArticles
where
(from w in db.WebObjects
from r in w.RelatedWebObjects
where w.Id == id
select r.Id).Contains(e.Id)
select e).OrderBy(x => x.Title).ToList();
List<MlaArticle> assignedWebObjects = (from e in db.MlaArticles
where
(from w in db.WebObjects
from r in w.RelatedWebObjects
where w.Id == id
select r.RelatedWebObjectIds).Any(i => i == e.Id)
select e).OrderBy(x => x.Title).ToList();

linq count/groupby not working

I want to group by the categoryid and then do a count on this. But I don't know how to do this. I have tried a couple of ways without success. Here is my latest:
public class Count
{
public int TradersCount { get; set; }
public int Id { get; set; }
public string Description { get; set; }
}
public IQueryable<Count> CountTradersAttachedToCategories()
{
var data = from tc in _db.tblTradersCategories
select new Count
{
Description = tc.tblCategory.description,
Id = tc.tblCategory.categoryId,
TradersCount = tc.Select(x => x.categoryid).GroupBy().Count()
};
return data;
}
tblTradersCategories joins both
tblTraders/tblCategories
A single trader can have many categories
A single category can have many traders
Thanks in advance for any help.
Clare
Try this:
var data = from tc in _db.tblTradersCategories
group tc by new { tc.tblCategory.categoryId,
tc.tblCategory.description } into g
select new { Count = g.Count(),
Id = g.Key.categoryId,
Description = g.Key.description };
If you want that in your Count class you may need to use AsEnumerable() to perform the conversion in process:
var converted = data.AsEnumerable()
.Select(c => new Count { TradersCount = c.Count,
Id = c.Id,
Description = c.Description });
You can try doing them all in one go:
var data = from tc in _db.tblTradersCategories
group tc by new { tc.tblCategory.categoryId,
tc.tblCategory.description } into g
select new Count { TradersCount = g.Count,()
Id = g.Key.categoryId,
Description = g.Key.description };
But I don't know if that will work. It depends on how the LINQ provider handles it.

Resources