Get Distinct List using LINQ - c#

I want to translate this query in LINQ format:
select m.MenuName,m.ParentID from Menu m where Id in(
select distinct m.ParentID from Menu m inner join MenuRole mr on mr.MenuID=m.Id)
This is what I have tried
var _employee = _db.Employees.AsEnumerable().Where(e => e.Id == Int32.Parse(Session["LoggedUserId"].ToString()))
.FirstOrDefault();
var _dashboardVM = new DashboardVM
{
MenuParentList = _employee.Designation.Role.MenuRoles
.Select(x => new SMS.Models.ViewModel.DashboardVM.MenuParent
{
MenuParentID=x.Menu.ParentID ,
MenuParentName=x.Menu.MenuName
})
.Distinct().ToList()
};
I am getting all list instead of distinct List
Dashboard VM
public class DashboardVM
{
public class MenuParent
{
public int? MenuParentID { get; set; }
public string MenuParentName { get; set; }
}
public List<MenuParent> MenuParentList { get; set; }
public List<Menu> MenuList { get; set; }
public User User { get; set; }
}

The Distinct() method checks reference equality for reference types. This means it is looking for literally the same object duplicated, not different objects which contain the same values.
Can you try the following? You may need to tweek as I have no testing environment:
MenuParentList = _employee.Designation.Role.MenuRoles.GroupBy ( r => r.Menu.ParentID + r.Menu.MenuName ).
.Select (y => y.First ())
.Select(x => new SMS.Models.ViewModel.DashboardVM.MenuParent
{
MenuParentID=x.Menu.ParentID ,
MenuParentName=x.Menu.MenuName
}).ToList();

Related

Simplify querying SQL in Entity Framework Core

I have the following code:
public async Task<IEnumerable<Submission>> SelectSubmissionsAsync(string submitterId, IEnumerable<Group> groups)
{
var submissions = new List<Submission>();
var apps = context.Apps
.Select(a => new
{
Id = a.Id,
Member = a.MemberHistories.OrderByDescending(ash => ash.MemberChangeDate).FirstOrDefault().Member,
Owner = a.OwnerHistories.OrderByDescending(oh => oh.OwnerChangeDate).FirstOrDefault().Owner
})
.ToDictionary(x => x.Id, x => x.Member + x.Owner);
var subs = context.Submissions.ToList();
foreach (var sub in subs)
{
if (apps.ContainsKey((Guid)sub.AppId))
{
var value = apps[(Guid)sub.AppId];
var check = value.Contains(submitterId, StringComparison.InvariantCultureIgnoreCase) || groups.Any(g => value.Contains(g.Id, StringComparison.InvariantCultureIgnoreCase));
if (check)
submissions.Add(sub);
}
}
}
public class Submission
{
public Guid Id { get; set; }
public Application App { get; set; }
public Guid? AppId { get; set; }
}
public class App
{
public Guid Id { get; set; }
public string Identifier { get; set; }
public ICollection<MemberHistory> MemberHistories { get; set;}
public ICollection<OwnerHistory> OwnerHistories { get; set;}
}
Is there a way to simplify this code (avoid for loop for example)?
Ideally you should be able to construct a single query looking something like this:
var appInfo = context.Apps
.Select(a => new
{
Id = a.Id,
Member = a.MemberHistories.OrderByDescending(ash => ash.MemberChangeDate).FirstOrDefault().Member,
Owner = a.OwnerHistories.OrderByDescending(oh => oh.OwnerChangeDate).FirstOrDefault().Owner
})
.Where(appCriteria)
;
var submissions = context.Submissions
.Where(s => appInfo.Any(app => s.AppId == app.Id))
.ToList();
That will allow your app to build a single SQL command that filters the apps down to just the ones you want before bringing them back from the database.
Building checkCriteria will be complicated, because that's going to be based on the "OR"/Union of several criteria. You'll probably want to build a collection of those criteria, and then combine them using a strategy similar to what I've defined here. If you start with a collection of values including submitterId and groupIds, each criteria would be something like s => s.Member == val || s.Owner == val.
In order to create these expressions, you'll probably need to declare a class to represent the type that you're currently using an anonymous type for, so you have a name to associate with the generic arguments on your Expression types.

How can I update the contents of a list with data from another list?

I have a list of two properties named Kanjis :
and I would like to use that to update the contents of another list named PhraseSources
public class PhraseSource
{
[PrimaryKey]
public string Id { get; set; }
public int PhraseNum { get; set; }
public string English { get; set; }
public string Kanji { get; set; }
public string WordType { get; set; }
public string FrequencyA { get; set; }
}
by matching together Kanji > Text and updating FrequencyA with Code
Is this something that can be done with LINQ or is there a better to way to do this by iterating through each of the rows of the phraseSource, checking for a matching entry in Kanjis and doing an update that way?
Here's the code suggested by Salva that I tried:
(from sa in source
join d in psDb on sa.Text equals d.Kanji
let temp = d.FrequencyA = sa.Code
select 0).ToList();
gives error:
ApplyFrequency.cs(14,14): Error CS1941: The type of one of the
expressions in the join clause is incorrect. Type inference failed in
the call to 'Join'. (CS1941) (Download)
source.Join(psDb, s => s.Text, d => d.Kanji, (s, d) => d.FrequencyA = s.Code).ToList();
gives error:
ApplyFrequency.cs(21,21): Error CS0411: The type arguments for method
'Enumerable.Join(IEnumerable,
IEnumerable, Func, Func,
Func)' cannot be inferred from the usage. Try
specifying the type arguments explicitly. (CS0411) (Download)
Notes:
I had to use this code as I was asked to change FrequencyA to an int:
var source = original
.Select(x => new
{
Text = x.Text,
Code = Convert.ToInt32(x.Code.Substring(2))
})
.ToList();
You can do it via single linq query syntax:
var source = new List<TextCodeClass>();
var dest = new List<PhraseSource>();
(from s in source
join d in dest on s.Text equals d.Kanji
let temp = d.FrequencyA = s.Code.ToString()
select 0).ToList();
or via method syntax:
source.Join(dest, s => s.Text, d => d.Kanji, (s, d) => d.FrequencyA = s.Code.ToString())
.ToList();
You can update list through LINQ only. The closest you could get is:
static void Main(string[] args)
{
List<SomeClass> scl = new List<SomeClass>();
List<OtherClass> ocl = new List<OtherClass>();
foreach (var item in scl)
item.FrequencyA = ocl.Where(i => i.Text == item.Kanji).FirstOrDefault()?.Code ?? null;
}
// sample classes that reflect relevant properties
public class SomeClass
{
public string FrequencyA { get; set; }
public string Kanji { get; set; }
}
public class OtherClass
{
public string Code { get; set; }
public string Text { get; set; }
}

Linq filtering nested collection with keeping the parent entity

I would like to get clients and their orders with cost > 100, which means I want to access client.Orders and fetch only those that have cost > 100.
var list1 = list.SelectMany(c => c.Orders.Where(x => x.Cost > 100), (c,x)=> c).Distinct();
public class Client
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public bool ClubMember { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int ItemCount { get; set; }
public int Cost { get; set; }
}
This returns all the clients who have A order with cost > 100, I want the orders to be filtered out and skip all the ones that do not match my condition. I tried it in some other ways, but did not succeed :( An explanation on the method would be fantastic to have as well.
I assume that you don't want to modify the content of your Client objects, here is my proposal:
var list2 = list.Select(c => new // 1
{
client = c,
orders = c.Orders.Where(x => x.Cost > 100)
})
.Where(a => a.orders.Any()) //2
.ToList();
1- Select a new entity which contains (for every initial client) a reference to the client along with a filtered list of its Orders
2- Keep only objects where there is at least one Order (already filtered)
Maybe so:
var clientsWithOnlyOneCost100 = clients.Select(c => new
{
client = c,
orders = c.Orders.Where(o => o.Cost > 100)
})
.Where(x => x.orders.Count() == 1);

How can I link two classes, select and still get a list output using Entity Framework and LINQ?

I have two objects: PhraseCategory and Phrase. Here's the classes:
public class PhraseCategory
{
public System.Guid PhraseCategoryId { get; set; } // PhraseCategoryId
public int PhraseCategoryShortId { get; set; } // PhraseCategoryShortId (Primary key)
public int PhraseCategoryGroupId { get; set; } // PhraseCategoryGroupId
public string Name { get; set; } // Name (length: 20)
// Reverse navigation
public virtual System.Collections.Generic.ICollection<Phrase> Phrases { get; set; } // Phrase.FK_PhrasePhraseCategory
}
public class Phrase : AuditableTable
{
public System.Guid PhraseId { get; set; } // PhraseId (Primary key)
public string English { get; set; } // English
public int? CategoryId { get; set; } // CategoryId
// Foreign keys
public virtual PhraseCategory PhraseCategory { get; set; } // FK_PhrasePhraseCategory
}
Can someone tell me how I could join these so that I am able to select all the phrases with for example a PhraseCategoryGroupId of 25.
Here's what I have right now but it does not take into account my need to also be able to select the Phrases with a PhraseCategory that has a PhraseCategoryGroupId:
List<Phrase> phrases;
var query = db.Phrases.AsQueryable();
if (options.CreatedBy != 0) query = query
.Where(w => w.CreatedBy == options.CreatedBy);
phrases = await query
.AsNoTracking()
.ToListAsync();
return Ok(phrases);
Note that I would like to get just a flat output (hope that makes sense). What I mean is a list that contains just:
PhraseId, English and CategoryId
This should get you what you need:
phrases = phrases.Where( x => x.PhraseCategory.PhraseCategoryGroupId == 25 )
.Select( x => new
{
PhraseId = x.PhraseId,
English = x.English,
CategoryId = x.CategoryId
});
Please note that you can also create instances of another type instead of the anonymous type which I am creating in the above query.
Also, the PhraseCategory will be lazy loaded in the above query since you have lazy loading enabled on the property: it is virtual. If you have lazy loading disabled globally, then you will need to use the Include method in your query. Then your query will become:
phrases = phrases.Include(x => x.PhraseCategory)
.Where( x => x.PhraseCategory.PhraseCategoryGroupId == 25 )
.Select( x => new
{
PhraseId = x.PhraseId,
English = x.English,
CategoryId = x.CategoryId
});

How can I group by a navigation property member in linq to sql?

I have three tables holding Users Groups and their association, UserGroups as laid out in this fiddle:
I am trying to obtain the maximum level among the users' groups as shown in the query in the fiddle using linq2sql.
However, EntityFramework obfuscates the join table, TblUserGroup and instead just gives me the navigation properties: TblGroups.Users or User.TblGroups
This is what I have put together thus far but Linqpad tells me it cannot execute:
var maxGroup = from ua in ctx.TblGroups
group ua by ua.TblUsers.Select(s=>s.UserId)
into g
select new
{
UserId= g.Key,
MaxLevel = g.Max(s => s.GroupLevel)
};
Seems you can do it like this:
var result = users.Select(u => new
{
UserId = u.Id,
MaxLevel = u.Groups.Max(g => g.GroupLevel)
});
Having:
class User
{
public int UserId { get; set; }
public string UserName { get;set; }
public List<Group> Groups { get; set; }
}
class Group
{
public int GroupId { get; set; }
public string GroupName { get; set; }
public int GroupLevel { get; set; }
public List<User> Users { get; set; }
}
Does it work for you?
var maxGroup = ctx.TblUsers
.Where(u => u.TblUserGroups != null)
.Select(u => new
{
UserId = u.UserId,
MaxGroupLevel = u.TblUserGroups.TblGroups.Max(g => g.GroupLevel)
}
);

Categories

Resources