How to bind linq data into Model class - c#

I have following query,
var userTree = (from user in users
join location in locations on user.FkHomeLocationId equals location.LocationId
join region in regions on location.RegionId equals region.LocationId
group region by new {
Regionid = region.LocationId,
location.LocationId
}
into grp
select new {
RegionName = regions.FirstOrDefault(s = >s.LocationId == grp.Key.Regionid).LocationName,
Branches = new {
BranchName = locations.FirstOrDefault(b = >b.RegionId == grp.Key.Regionid).LocationName,
Users = users.Where(u = >u.FkHomeLocationId == grp.Key.LocationId).Select(s = >new {
FullName = s.FirstName + " " + s.LastName
}).ToList()
}
}).ToList();
I want to bind the query result into model. how can I do this. I created following class model to achieve that,
public class TreeModel
{
public string RegionName { get; set; }
public Branch Branchs { get; set; }
}
public class Branch
{
public string BranchName { get; set; }
public List<User> Users { get; set; }
}
public class User
{
public string FirstName { get; set; }
}
Now need to bind above query data into this model class? Have any best way to do this without creating Branch and User classes? because my application have another classes with the same name of User and Branch and I feel some guilty to create separate classes like this for query data binding? I'm I correct?
Updated: I tried as follows,
var userTree = (from user in users
join location in locations on user.FkHomeLocationId equals location.LocationId
join region in regions on location.RegionId equals region.LocationId
group region by new {
Regionid = region.LocationId,
location.LocationId
}
into grp
select new Application.Alerts.Models.AlertUserTreeViewModel() {
RegionName = regions.FirstOrDefault(s = >s.LocationId == grp.Key.Regionid).LocationName,
Branchs = new Application.Alerts.Models.Branch() {
BranchName = locations.FirstOrDefault(b = >b.RegionId == grp.Key.Regionid).LocationName,
Users = new Application.Alerts.Models.User {
FirstName = users.Where(u = >u.FkHomeLocationId == grp.Key.LocationId).FirstOrDefault().FirstName
}
}
}).ToList();
But I'm getting compile error in this line,
Users = new Application.Alerts.Models.User {
FirstName = users.Where(u = >u.FkHomeLocationId == grp.Key.LocationId).FirstOrDefault().FirstName
}
as
Cannot implicitly convert type 'Alerts.Models.User' to 'System.Collections.Generic.List<Alerts.Models.User>'

just add extra FirstName property to your anonymous classes
var userTree =
....
select new {
RegionName = ...
Branches = new {
BranchName = ...,
Users = ....Select(s = >new {
FirstName=s.FirstName,
FullName = s.FirstName + " " + s.LastName
}).ToList()
}
after this you can create view model
var treeModel= userTree.Select( new TreeModel {
RegionName = userTree.RegionName
Branchs =userTree.Branches.Select(i=>
new Branch {
BranchName= i.BranchName,
Users= i.Users.Select(j=> new User{ FirstName=j.Firstname})
)});
if you need only fullname in your userTree you can convert back
userTree= userTree.Select( new TreeModel {
RegionName = userTree.RegionName
Branchs =userTree.Branches.Select(i=>
new Branch {
BranchName= i.BranchName,
Users= i.Users.Select( j=> new {j.FullName})
)});
UPDATE
if you want to run another query using dbcontext you can try this
var userTree = (from user in users
join location in locations on user.FkHomeLocationId equals location.LocationId
join region in regions on location.RegionId equals region.LocationId
group region by new {
Regionid = region.LocationId,
location.LocationId
}
into grp
select new Application.Alerts.Models.AlertUserTreeViewModel {
RegionName = regions.FirstOrDefault(s = >s.LocationId == grp.Key.Regionid).LocationName,
Branches = new new Application.Alerts.Models.Branch {
BranchId = locations.FirstOrDefault(b = >b.RegionId ==
grp.Key.Regionid).LocationId,
BranchName = locations.FirstOrDefault(b = >b.RegionId == grp.Key.Regionid).LocationName,
Users = users.Where(u = >u.FkHomeLocationId == grp.Key.LocationId).Select(s = >new User {
Id=s.Id,
LastName=s.LastName,
FirstName= s.FirstName
}).ToList()
}
}).ToList();

Your Branch has a List<User> but you are trying to instantiate as a User that's why you are getting the error. Use below code.
Users = users.Where(u = >u.FkHomeLocationId == grp.Key.LocationId).Select(u => new User { FirstName = u.FirstName }).ToList()

Related

Linq query with Average

I have 3 tables:
tblCompany : Id, Name, Location
tblRating : Id, CompanyId, Rate
tblImages : Id, CompanyId, ImagePath
I have a class
public class Company
{
public string Id { get; set; }
public string CompanyName { get; set; }
public string Location { get; set; }
public string AverageRate { get; set; }
public List<string> ImagePath { get; set; }
}
I want a LINQ query to produce a result to match the Company class.
I wrote this query but it does not work
List<Company> result = null;
using (DataContext dc = new DataContext())
{
result = (from a in dc.GetTable<tblCompany>()
join b in dc.GetTable<tblRating>()
on a.Id equals b.CompanyId
join c in dc.GetTable<tblImages>()
on a.Id equals c.CompanyId
select new SearchResult
{
CompanyName = a.Company,
Location = a.Location,
AverageRate = b.Rate.Average(),
ImagePath = c.ImagePath.ToList()
}).ToList<Company>();
}
Edited for whole Query:
(I've to say i'm sorry but i have no way of testing this query yet)
You can use the let clause instead of the joins:
var result = (from c in dc.GetTable<tblCompany>()
let r = (from re in dc.GetTable<tblRating>()
where re.CompanyId == c.Id && re.Rate != null
select re.Rate)
let i = (from im in dc.GetTable<tblImages>()
where im.CompanyId == c.Id
select im.ImagePath)
select new SearchResult
{
CompanyName = c.Name,
Location = c.Location,
AverageRate = r.Average(),
ImagePath = i.ToList()
}).ToList<Company>();
try this -
result = (from a in dc.GetTable<tblCompany>()
join b in dc.GetTable<tblRating>()
on a.Id equals b.CompanyId
join c in dc.GetTable<tblImages>()
on a.Id equals c.CompanyId
group new { b.Rate, c.ImagePath}
by new { a.Id, a.Location,a.Name} into groupList
select new Company
{
CompanyName = groupList.Key.Name,
Location = groupList.Key.Location,
AverageRate = groupList.Average(a=>a.Rate),
ImagePath = groupList.Select(i=>i.ImagePath).ToList()
}).ToList<Company>();

Merging 2 lists of different objects into a third list of a different type?

I'm building an API wrapper for a third party API that translates their objects into business domain objects that can be used for other processing. In this case, I need to take 2 different objects Contact and User and merge them into a single list of objects called UserContacts. I'm matching these objects based on their Email property, and if there is no matching elements, a new one is inserted.
Here are my current objects and methods, I'm just trying to figure out if there's a better/faster method.
public class ContactUser : IUser
{
public string SalesForceUserId { get; set; }
public string SalesForceContactId { get; set; }
public string ZendeskId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
private List<IUser> MergeContactsAndUsers()
{
var sfContacts = SalesForceCache.Contacts.Data;
var sfUsers = SalesForceCache.Users.Data;
var newUsers = sfUsers.Select(user => new ContactUser
{
SalesForceUserId = user.Id,
Name = user.Name,
FirstName = user.FirstName,
LastName = user.LastName,
Email = user.Email
}).Cast<IUser>().ToList();
foreach (var contact in sfContacts)
{
var tmp = newUsers.FirstOrDefault(n => n.Email == contact.Email);
if (tmp != null)
{
tmp.SalesForceContactId = contact.Id;
}
else
{
var newUser = new ContactUser
{
SalesForceContactId = contact.Id,
Name = contact.Name,
FirstName = contact.FirstName,
LastName = contact.LastName,
Email = contact.Email
};
newUsers.Add(newUser);
}
}
return newUsers;
}
If you want to replace your current implementation with Join you can have something like this:
private List<IUser> MergeContactsAndUsers()
{
var sfContacts = SalesForceCache.Contacts.Data;
var sfUsers = SalesForceCache.Users.Data;
var leftJoinResults =
sfUsers.Join(
sfContacts,
u => u.Email,
c => c.Email,
(u, c) => new ContactUser()
{
SalesForceContactId = c.SalesForceContactId,
SalesForceUserId = u.Id,
Name = u.Name,
FirstName = u.FirstName,
LastName = u.LastName,
Email = u.Email
}).Cast<IUser>().ToList();
var rightJoinResults =
sfContacts
.Where(c => !leftJoinResults.Select(nu => nu.SalesForceContactId).Contains(c.Id))
.Select(c => new ContactUser
{
SalesForceContactId = c.Id,
Name = c.Name,
FirstName = c.FirstName,
LastName = c.LastName,
Email = c.Email
});
leftJoinResults.AddRange(rightJoinResults);
return leftJoinResults;
}
But because Join is only a left join (and you need right join as well) it still requires an additional query to get missing contacts (the query to get rightJoinResults).
It's more of an alternative implementation with use of Join. Without proper measurements it's hard to tell whether it's faster.

How to populate `ICollection<T>` property inside a `List<T>` linq

My Class
public partial class CTITLE_CHECKLIST : CError
{
public int Id { get; set; }
[Required(ErrorMessage = "Requerido")]
public int ID_Tipo_Checklist { get; set; }
[Required(ErrorMessage = "Requerido")]
public string Descripcion { get; set; }
public virtual CTipo_CheckList Tipo_CheckList { get; set; }
public ICollection<CSUBTITLE_CHECKLIST> Subtitulos { get; set; }
}
My method :
public List<CTITLE_CHECKLIST> GetCheked(string codigo, int tipoCheckList)
{
try
{
var result = (from a in db.TITLE_CHECKLIST
from t in db.Tipo_CheckList
where a.ID_Tipo_Checklist == t.ID
where a.ID_Tipo_Checklist == tipoCheckList
select new CTITLE_CHECKLIST
{
Descripcion = a.Descripcion,
Id = a.Id,
ID_Tipo_Checklist = a.ID_Tipo_Checklist,
Tipo_CheckList = new CTipo_CheckList
{
Descripcion = t.Descripcion,
ID = t.ID,
ID_Depto = t.ID_Depto
},
Subtitulos = (from s in db.SUBTITLE_CHECKLIST
where s.ID_Title == a.Id
select new CSUBTITLE_CHECKLIST
{
AmountCK = s.AmountCK,
Descripcion = s.Descripcion,
ID = s.ID,
ID_Title = s.ID_Title,
Numeracion = s.Numeracion,
checkList = (from ck in db.CheckList
where ck.ID_Subtitle == s.ID
&& ck.Codigo == codigo
select new CCheckList
{
CK = ck.CK,
Amount = ck.Amount,
Codigo = ck.Codigo,
Codigo_TFile = ck.Codigo_TFile,
Comentario = ck.Comentario,
ID = ck.ID,
ID_Subtitle = ck.ID_Subtitle,
UserCre = ck.UserCre,
UserMod = ck.UserMod
}).FirstOrDefault()
})//here I put ToList
}).ToList();
An my error this :
Error 2 Cannot implicitly convert type 'System.Linq.IQueryable<TROP.Areas.TRAFICO.Controllers.LOGICA.CSUBTITLE_CHECKLIST>' to 'System.Collections.Generic.ICollection<TROP.Areas.TRAFICO.Controllers.LOGICA.CSUBTITLE_CHECKLIST>'. An explicit conversion exists (are you missing a cast?) C:\Users\jmitchell\Documents\Visual Studio 2012\Projects\TROP\TROP\Areas\TRAFICO\Controllers\LOGICA\CTITLE_CHECKLIST.cs 136 49 TROP
And When I put .ToList, where say Here I put ToList it throw an error that say :
LINQ to Entities does not recognize the method 'System.Collections.Generic.List`1[TROP.Areas.TRAFICO.Controllers.LOGICA.CSUBTITLE_CHECKLIST] ToList[CSUBTITLE_CHECKLIST](System.Collections.Generic.IEnumerable`1[TROP.Areas.TRAFICO.Controllers.LOGICA.CSUBTITLE_CHECKLIST])' method, and this method cannot be translated into a store expression.
And I Know that if I change the property subtitles to IEnumerable<T> it will work, but, Im trying to bind this model from MVC4 post, And I doesn't bind with IEnumerable<T>, it bind with a ICollection<T>, List<T> , I am like 1 and a half day trying to figure out this problem.
So the choices are not "do the whole thing in the database" or "do the whole thing in memory". You want to do some of both. Do everything on the DB end that you can, and then when you've done all of that, finish up the remaining operations in memory. This general pattern (mostly leveraging AsEnumerable) will allow you to do this:
(from a in db.TITLE_CHECKLIST
from t in db.Tipo_CheckList
where a.ID_Tipo_Checklist == t.ID
where a.ID_Tipo_Checklist == tipoCheckList
select new //note were using an anonymous type here,
//as the real type can't take a non-list
{
Descripcion = a.Descripcion,
Id = a.Id,
ID_Tipo_Checklist = a.ID_Tipo_Checklist,
Tipo_CheckList = new CTipo_CheckList
{
Descripcion = t.Descripcion,
ID = t.ID,
ID_Depto = t.ID_Depto
},
Subtitulos = from s in db.SUBTITLE_CHECKLIST
where s.ID_Title == a.Id
select new CSUBTITLE_CHECKLIST
{
AmountCK = s.AmountCK,
Descripcion = s.Descripcion,
ID = s.ID,
ID_Title = s.ID_Title,
Numeracion = s.Numeracion,
checkList = (from ck in db.CheckList
where ck.ID_Subtitle == s.ID
&& ck.Codigo == codigo
select new CCheckList
{
CK = ck.CK,
Amount = ck.Amount,
Codigo = ck.Codigo,
Codigo_TFile = ck.Codigo_TFile,
Comentario = ck.Comentario,
ID = ck.ID,
ID_Subtitle = ck.ID_Subtitle,
UserCre = ck.UserCre,
UserMod = ck.UserMod
}).FirstOrDefault()
}//note no ToList
})
//This will ensure that all operators that follow
//are done in memory, not on the DB end
.AsEnumerable()
.Select(checklist => new CTITLE_CHECKLIST
{
Descripcion = checklist.Descripcion,
Id = checklist.Id,
ID_Tipo_Checklist = checklist.ID_Tipo_Checklist,
Subtitulos = Subtitulos.ToList(),
});

LINQ LEFT JOIN, Combine multiple WHERE clauses with OR

LINQ,
var clause = PredicateBuilder.False<User>();
clause = clause.Or(u => u.uid.Equals(1));
clause = clause.Or(u => u.uid.Equals(2));
var usersInGroup = (from u in db.Users
join g in db.GroupUsers
on u.uid equals g.uid
into ug
from g in ug.DefaultIfEmpty()
where g.gid.Equals(0)
select u).Where(clause);
These two where clauses are chained together as;
WHERE ([t0].[gid] = 0) AND (([t1].[uid] = 1) OR ([t1].[uid] = 2))
How do I add the two where conditions as
WHERE ([t0].[gid] = 0) OR (([t1].[uid] = 1) OR ([t1].[uid] = 2))
Thanks to,
Can PredicateBuilder generate predicates that span multiple tables?
I now have a solution that works but my result set is based on a new hybrid class. As a result I have had to mirror all of the relevant fields. See below.
public class HybridGroupUser {
private User _user;
public User User {
get { return _user; }
set {
_user = value;
if (value != null) {
uid = value.uid;
fname = value.fname;
lname = value.lname;
email = value.email;
}
}
}
private GroupUser _GroupUser;
public GroupUser GroupUser {
get { return _GroupUser; }
set {
_GroupUser = value;
if (value != null) {
uid = value.uid;
fname = value.fname;
lname = value.lname;
email = value.email;
}
}
}
public int? uid { get; set; }
public string fname { get; set; }
public string lname { get; set; }
public string email { get; set; }
}
With this class I can now do the following;
var clause = PredicateBuilder.False<HybridGroupUser>();
clause = clause.Or(u => u.GroupUser.gid.Equals(0);
foreach (int i in AddedUsers) {
int tmp = i;
clause = clause.Or(u => u.User.uid.Equals(tmp));
}
var usersInGroup = (from u in db.Users
join gusr in db.GroupUser
on u.uid equals gusr.uid
into ug
from gusr in ug.DefaultIfEmpty()
select new HybridGroupUser {
User = u,
GroupUser = gusr
}).Where(clause);
var usersInGroup = (from u in db.Users
join g in db.Groups
on u.uid equals g.uid
where g.gid.Equals(0) || (u.uid.Equals(1) || u.uid.Equals(2))
select u)
Instead of doing multiple Or clauses, why not just do Contains. That way your list of Ids can be complete dynamic (as long as it doesn't exceed 2000):
var ids = new int[] {1, 2, 3};
var usersInGroup = (from u in db.Users
join g in db.GroupUsers
on u.uid equals g.uid
into ug
from g in ug.DefaultIfEmpty()
where g.gid.Equals(0)
&& ids.Contains(u.uid)
select u);

How do I match two identical database tables with LINQ?

I want to match 2 identical tables:
sourceProducts (productName, ProductionDate, ManID, shipper, distributer)
CommProducts (productName, ProductionDate, ManID, shipper, distributer)
but the number of rows and the record contents may differ. How do I select a certain record = raw from one table and get its clone record from the other table (e.g., check if the same record exists)? How do I do this using LinQ?
UPDATE: Here's the LINQ code:
protected void checkBtn_Click(object sender, EventArgs e)
{
MyProductsDataContext mySdb = new MyProductsDataContext();
Product mypro = new Product { ManId = int.Parse(TxtManI.Text), ProductName = TxtProN.Text, ProductionDate =DateTime .Parse ( TxtProDat.Text), Shipper = TxtShipI.Text, Distributer = TxtDistI.Text };
var spro = (from p in mySdb.Products
select new { p.ManId, p.ProductName, p.ProductionDate, p.Shipper, p.Distributer }).
Intersect(from s in mySdb.SourceProducts select new { s.ManId, s.ProductName, s.ProductionDate, s.Shipper, s.Distributer });
if (spro != null)
{
LblMessage.Text = "Acceptable product Data Inserted Sucessfully";
InsertData();
}
else
{
LblMessage.Text = "Invalid Product or bad Entry Please retype";
}
}
I would join on ManId and then compare the rest of the values in a where clause:
bool productExists = (
from p in mySdb.Products
join s in mySdb.SourceProducts
on p.ManId equals s.ManId
where p.ProductName == s.ProductName
&& p.ProductionDate == s.ProductionDate
&& p.Shipper == s.Shipper
&& p.Distributer = s.Distributer
select new { p.ManId, p.ProductName, p.ProductionDate, p.Shipper, p.Distributer }
).Any();
if (productExists)
{
LblMessage.Text = "Acceptable product Data Inserted Sucessfully";
InsertData();
}
else
{
LblMessage.Text = "Invalid Product or bad Entry Please retype";
}
I've used Any() to produce an efficient EXISTS SQL query. You could use SingleOrDefault() or FirstOrDefault() instead if you actually need to use the product returned.
I also don't see anywhere that you're using your new Product's ID - you might need to add that filter to the query as well:
Product mypro = new Product { ... };
bool productExists = (
from p in mySdb.Products
where p.ManId equals mypro.ManId
join s in mySdb.SourceProducts
on p.ManId equals s.ManId
...
You can probably do this using a join but I've hobbled together a unit test which shows one way to this
public class TestProduct
{
public int ManId { get; set; }
public string ProductName { get; set; }
public DateTime ProductionDate { get; set; }
public string Shipper { get; set; }
public string Distributor { get; set; }
}
[TestMethod]
public void TestSourceTable()
{
// Set up a test list
var list = new List<TestProduct>();
for (int i=0;i<5;i++)
{
var p = new TestProduct
{
Distributor = "D" + i,
ManId = i,
ProductionDate = DateTime.Now,
ProductName = "P" + i,
Shipper = "S" + i
};
list.Add(p);
}
// Get an existing product
var existingProduct = list[4];
// Get an unknown product
var unknownProduct = new TestProduct()
{
ManId = -1,
Distributor = "",
ProductionDate = DateTime.Now.AddDays(-1),
ProductName = "",
Shipper = ""
};
// product found
Assert.True(list.Any(p => p == existingProduct));
// product not found
Assert.False(list.Any(p => p == unknownProduct));
}

Categories

Resources