Getting join results using LINQ to SQL - c#

I'm new to LINQ. I'm trying to join two tables, but I have difficulties returning the results.
Here is my code:
using (DatabaseDataContext db = new DatabaseDataContext())
{
var list = db.Products.Join(db.ProductDetails,
p => p.ID,
d => d.ProductID,
(p, d) => new {
p.ID,p.Photo,d.Name,d.LanguageID
}).Where(d=>d.LanguageID== lang).ToList();
}
Well I can not use the variable list outside using and when I declare the variable outside 'using' (before it) like: var list;.
I get the error:
Implicitly-typed local variables must be initialized
Update:
I changed the code to:
DatabaseDataContext db = new DatabaseDataContext();
var products = db.Products.Join(db.ProductDetails,
p => p.ID,
d => d.ProductID,
(p, d) => new {
p.ID,p.Photo,d.Name,d.LanguageID
}).Where(d=>d.LanguageID== langs[language].ParseInt()).ToList();
and it worked. As I have omitted using, do I have to do anything like closing the connection?
Is there a problem not using the using?

If you don't use the results of the query in the same scope, you must make it typed so you can declare variables of appropriate type. First define a class for the result objects and use it. It would be cleaner to put this all as a method.
public class Result
{
public int ID { get; set; }
public string Photo { get; set; }
public string Name { get; set; }
public int LanguageID { get; set; }
}
public List<Result> GetJoinResult(int languageId)
{
using (DatabaseDataContext db = new DatabaseDataContext())
{
return db.Products.Join(db.ProductDetails, p => p.ID, d => d.ProductID,
(p, d) => new Result // not anonymous
{
ID = p.ID,
Photo = p.Photo,
Name = d.Name,
LanguageID = d.LanguageID,
})
.Where(x => x.LanguageID == languageId)
.ToList();
}
}
If defining the types is too much, then you must use it immediately in the same scope.
using (DatabaseDataContext db = new DatabaseDataContext())
{
var results = db.Products.Join(db.ProductDetails, p => p.ID, d => d.ProductID,
(p, d) => new
{
p.ID,
p.Photo,
d.Name,
d.LanguageID,
})
.Where(x => x.LanguageID == languageId)
.ToList();
// do stuff with results
}

Related

How to convert Generic List<anonymous type > to Generic List <SubToSubMenu>?

I have a problem with type convert var to List<SubToSubMenu>.
Firstly, I select data from database that is ok !!!!. I received data with var variable, but i can't convert var type to List<SubToSubMenu> data type.
This is my LINQ statement:
var ss =
db
.SubToSubMenus
.Join(
db.MenuPermissions,
s => s.ID,
p => p.SubToSubMenuId,
(s, p) => new { s, p })
.Where(w => w.s.Active == true && w.p.RoleId == roleId && w.p.hasPermission == true)
.Select(s => new
{
ID = s.s.ID,
SubToSubMenuName = s.s.SubToSubMenuName,
Description = s.s.Description,
})
.ToList();
This is SubToSubMenu class:
[Table("SubToSubMenus")]
public class SubToSubMenu : AceEntityBase
{
public SubToSubMenu()
{ }
[Key]
public string ID { get; set; }
public string SubToSubMenuName { get; set; }
public string Description { get; set; }
public string SubMenuID { get; set; }
}
var is not a type, it's syntactic sugar. You have an anonymous type that has no relation whatsoever to your SubToSubMenu type.
Instead of projecting into an anonymous type:
.Select(s => new { ... })
Project into the type you want:
.Select(s => new SubToSubMenu { ... })
You shouldn't need to project here. You're already selecting the SubToSubMenu from the database, so there should be no need to 'recreate' the class later in the expression chain.
var ss =
db
.SubToSubMenus
.Join(
db.MenuPermissions,
s => s.ID,
p => p.SubToSubMenuId,
(s, p) => new { s, p })
.Where(w => w.s.Active == true && w.p.RoleId == roleId && w.p.hasPermission == true)
This is good so far. You've joined two tables and applied the correct filters.
.Select(s => new
{
ID = s.s.ID,
SubToSubMenuName = s.s.SubToSubMenuName,
Description = s.s.Description,
})
.ToList();
OK stop here. If the ultimate goal of this query is to select only SubToSubMenu entities, you can replace this part with just
.Select(s => s.s);
...and ignore the rest of the subsequent statements.
However, you could also go one step further and make the association between the SubToSubMenu and MenuPermissions entities implicit in your EF configuration, so you'll have no need to .Join in LINQ. Given this, the eventual query should be similar to:
var ss = db.SubToSubMenus
.Where(stsm => stsm.Active
&& stsm.MenuPermissions.RoleId == roleId
&& stsm.MenuPermissions.HasPermission);
Try this:
var ss =
db
.SubToSubMenus
.Join(
db.MenuPermissions,
s => s.ID,
p => p.SubToSubMenuId,
(s, p) => new { s, p })
.Where(w => w.s.Active == true && w.p.RoleId == roleId && w.p.hasPermission == true)
.Select(s => new
{
ID = s.s.ID,
SubToSubMenuName = s.s.SubToSubMenuName,
Description = s.s.Description,
})
.ToList()
.Select(s => new SubToSubMenu()
{
ID = s.ID,
SubToSubMenuName = s.SubToSubMenuName,
Description = s.Description,
})
.ToList();
I have added a simple projection to the end of your query. This is to keep the code as close to how you had it to begin with and to aid with any future refactoring.
In this case, it can certainly be coded as a single projection.

Order by array values in Linq to Entity Framework Query

I am trying to write an OrderBy clause in a Linq to EntityFramework query. My problem is that the entity table I am looking at stores an ID, that relates to a table in a different database and I cannot adjust the database.
MainDatabase.EntityToOrder
ID
Name
OtherID
SecondDatabase.OtherEntity
ID
Name
My C# EntityToOrder Model looks like this, and I need to be able to order by "OtherName"
EntityToOrder.cs
public class EntityToOrder
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public long OtherId { get; set; }
public string OtherName { get; set; }
}
So, I would like to Order EntityToOrder by "OtherName" in the most efficient way possible. My existing query looks like this.
var entities = mainContext.EntityToOrder.OrderBy(e => e.Name).Skip(startIndex).Take(pageSize).ToList();
var otherIds = entities.Select(e => e.OtherID).ToList();
Dictionary<long, string> otherNames = secondContext.OtherEntity
.Where(oe => otherIds.Contains(oe.ID))
.Select(oe => new { ID = oe.ID, Name = oe.Name })
.ToDictionary(oe => oe.ID, oe => oe.Name);
entities.ForEach(e => OtherName = otherNames[e.OtherID]);
How can I write the most efficient query to order by "OtherName", preferably avoiding selecting the whole EntityToOrder table into memory.
Update
For clarity, here is some code that achieves the OrderBy, but needs to retrieve the entire EntityToOrder table into memory. I was hoping this could be achieved in a more efficient way. Also, the OtherEntity can belong to many EntityToOrder rows.
var entities = mainContext.EntityToOrder.ToList();
var otherIds = entities.Select(e => e.OtherID).ToList();
Dictionary<long, string> otherNames = secondContext.OtherEntity
.Where(oe => otherIds.Contains(oe.ID))
.Select(oe => new { ID = oe.ID, Name = oe.Name })
.ToDictionary(oe => oe.ID, oe => oe.Name);
entities.ForEach(e => OtherName = otherNames[e.OtherID]);
return entities.OrderBy(e => e.OtherName).Skip(startIndex).Take(pageSize).ToList();
Quite challenging task. I was thinking initially just to switch the roles and perform pagination (OrderBy/Skip/Take) on OtherEntity table, but unfortunately that doesn't work due to one to many relationship. So I ended up with doing some pre pagination in memory on OtherEntity. However, in order to do that I needed counts of the matching items in EnityToOrder, so this is retrieved with additional db query, which makes the solution involving 3 db queries and some memory processing. Here it is
var countByOtherId = db.EntityToOrder
.GroupBy(e => e.OtherId)
.Select(g => new { ID = g.Key, Count = g.Count() })
.ToDictionary(e => e.ID, e => e.Count);
var other = new Dictionary<long, string>();
int skipCount = startIndex, useCount = 0;
foreach (var e in db.OtherEntity.OrderBy(e => e.Name))
{
int count;
if (!countByOtherId.TryGetValue(e.ID, out count)) continue;
if (skipCount > 0 && other.Count == 0)
{
if (skipCount >= count) { skipCount -= count; continue; }
count -= skipCount;
}
other.Add(e.ID, e.Name);
if ((useCount += count) >= pageSize) break;
}
var entities = db.EntityToOrder
.Where(e => other.Keys.Contains(e.OtherId))
.AsEnumerable()
.Select(e => new EntityToOrder { ID = e.ID, Name = e.Name,
OtherId = e.OtherId, OtherName = other[e.OtherId] })
.OrderBy(e => e.OtherName).ThenBy(e => e.Name)
.Skip(skipCount).Take(pageSize)
.ToList();
Now, I'm not quite sure if that's better to what are you doing currently, but it's worth trying.
If you can change the model, then you might try the following:
public class EntityToOrder
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public long OtherId { get; set; }
[ForeignKey("OtherId")]
public OtherEntity OtherEntity{ get; set; }
}
Then, you should be able to perform this query:
using System.Data.Entity;
var entities = mainContext
.EntityToOrder
.Include(x => x.OtherEntity)
.OrderBy(e => e.OtherEntity.Name)
.Skip(startIndex)
.Take(pageSize)
.ToList();
Edit :
Sorry, I missed the point that you had 2 databases....
I found an alternative which I thought I would post in case it is useful to anyone. I used a .Join() to merge the dictionary of OtherEntity into my query. This still selects into an IEnumerable so I don't think it is more efficient.
var entities = mainContext.EntityToOrder;
var otherIds = entities.Select(e => e.OtherID).ToList();
Dictionary<long, string> otherNames = secondContext.OtherEntity
.Where(oe => otherIds.Contains(oe.ID))
.Select(oe => new { ID = oe.ID, Name = oe.Name })
.ToDictionary(oe => oe.ID, oe => oe.Name);
Func<EntityToOrder, KeyValuePair<long, string>, EntityToOrder> joinFunc = ((a, b) => {
a.OtherName= b.Value;
return a;
});
return entities.Join(otherNames, e => e.OtherID, oe => oe.Key, joinFunc)
.OrderBy(e => e.OtherName)
.Skip(startIndex)
.Take(pageSize)
.ToList();
Note on Includes
When applying Join you select into an IEnumerable and therefore lose the ability to access properties from a linked table. To counter this you would need to add a .Include() for any linked table you need to access before applying the .Join(). E.g.
var entities = mainContext.EntityToOrder
.Include("LinkedEntity");
return entities.Join(otherNames, e => e.OtherID, oe => oe.Key, joinFunc)
.OrderBy(e => e.OtherName)
.ThenBy(e => e.LinkedEntity.Name) //reference to linked table
.ToList();

Add GroupBy to Select

I have this query
[HttpGet]
public List<AttachedPhotosModel> GetReportAttachedPhotos(int reportId)
{
var photos = new ReportsRepository().GetInjuryPhotos(reportId);
return photos.Select(x => new AttachedPhotosModel()
{
Id = x.Id,
Type = x.InjuryType,
Photos = photos.Where(y => y.InjuryType == x.InjuryType).Select(z => z.ServicePhotoUrl).ToList()
}).ToList();
}
I need to GroupBy InjuryType, how to do this?
I added return photos.GroupBy(k => k.InjuryType).Select(x => new AttachedPhotosModel() but how to select model, x have new value key and I don't know how to select my data
This code should work. Assuming photos is collection of objects with InjuryType property and PhotoUrl property and AttachedPhotosModel has an InjuryType and Photos properties like this.
public class AttachedPhotosModel
{
public string InjuryType { set; get; }
public List<string> Photos { set; get; }
}
Code for grouping by InjurType.
var grouped = photos
.GroupBy(s => s.InjuryType,d => d.PhotoUrl, (k, g) => new
AttachedPhotosModel
{
InjuryType = k,
Photos = g.ToList()
}).ToList();

Cannot access members of a class in the select linq method

static void Main(){
List<Foo> t = new List<Foo>{
new Foo(){Id=1,Name="A",Value=1},
new Foo(){Id=2,Name="B",Value=1},
new Foo(){Id=3,Name="C",Value=1},
new Foo(){Id=3,Name="D",Value=1}};
var x = t.GroupBy(gp => gp.Id).Select(sel => new Foo { Id = ,Name=,Value= });
}
public class Foo{
public string Name { get; set; }
public int Id { get; set; }
public int Value { get; set; }
}
In the var x I want to group all the Foo objects by their ID and get the SUM in the Value field.
The problem is that it seems I cannot access the members/fields of the class in the select method.
Thanks
After GroupBy you don't select an IEnumerable<Foo> but groups of them. You probably want:
var x = t.GroupBy(f => f.Id)
.Select(grp => new Foo {
Id = grp.Key,
Name = String.Join(",", grp.Select(f => f.Name)),
Value = grp.Sum(f => f.Value)
});
I'm using String.Join to concenate all names of each ID-group, the values are summed.
Try this way
var x = t.GroupBy(gp => gp.Name).OrderBy(group => group.Key).Select(group => Tuple.Create(group.Key, group.Count()));

A way around LINQ to Entities does not recognize the method?

I've got a method I've been using against IEnumerable no problem. However I want to start using it against IQueryable as well. However, the way I currently have it wont work as its trying to execute my method against the database.
The situation is as follows. I want a property on the object I'm selecting into to be be null if the value selecting from is null or the Id and Name of the property if it exists. For example:
var foos = FooRepository.All().Select(s => new FooBrief()
{
Id = s.Id,
Customer = SimpleData.To(s.Customer, m => m.Id, m => m.Name)
});
where SimpleData.To looks like:
public class SimpleData
{
public int Id { get; set; }
public string Name { get; set; }
public static SimpleData To<T>(T t, Func<T, int> id, Func<T, string> name) where T : class
{
if (t != null)
{
return new SimpleData { Id = id(t), Name = name(t) };
}
return null;
}
}
Is there someway I can get this behaviour whilst allowing it to execute against the database?
NOTE: Because of reasons elsewhere in my code I cannot use .ToList(). I may be adding additional filtering at a later point
The simplest approach is just to perform the selection outside the database, using AsEnumerable:
var foos = FooRepository.All()
.Select(x => new { Id = x.Id,
CustomerId = x.Customer.Id,
CustomerName = x.Name })
.AsEnumerable()
.Select(s => new FooBrief {
Id = s.Id,
Customer = new SimpleData {
Id = s.CustomerId,
Name = s.CustomerName
}
});
The first Select is just to make sure that the database query only pulls out the required fields. If you really still want to use your SimpleData.To method:
// Outdented to avoid scrolling
var foos = FooRepository.All()
.Select(x => new { Id = x.Id,
CustomerId = x.Customer.Id,
CustomerName = x.Name })
.AsEnumerable()
.Select(s => new FooBrief {
Id = s.Id,
Customer = SimpleData.To(s,
s => s.CustomerId,
s => s.CustomerName)
});

Categories

Resources