I have built a short and sweet join query to try and get a feel on how to create a join. I managed to do it in SQL. But I'm not sure how to do it in LINQ.
LINQ:
public IQueryable<DepartmentBreakdownReport> GetDepartmentBreakdown
(int SupplierID, int ReviewPeriodID)
{
return (from detail in camOnlineDb.Details
join suppDepp in camOnlineDb.SuppDepts
on new { detail.ClientID, detail.CategoryID }
equals new { suppDepp.ClientID, suppDepp.CategoryID }
select detail.ClientID + "" + detail.CategoryID);
}
Edit: Ignore the parameters which are brought in, I will cater to those once I have my join working.
You are returning an IQueryable<string> rather than what I assume you want is IQueryable<DepartmentBreakdownReport>. To return that type, you need to project in the select by specifying the type, something like this:
return (from detail in camOnlineDb.Details
join suppDepp in camOnlineDb.SuppDepts
on new { detail.ClientID, detail.CategoryID }
equals new { suppDepp.ClientID, suppDepp.CategoryID }
select new DepartmentBreakdownReport
{
Property1 = detail.Property1,
//your properties here
});
The problem was that Category ID was nullable in Detail but not in suppDepp.
To fix it I changed it from a non-nullable type
I am having problem with converting the DateTime I am collecting from the database to localtime with LINQ. A LINQ query won't let me use ToLocalTime() and I can't seem to get any fix outside the query to work with the anonymous type of list.
Here is the LINQ query from the controller :
// GET: api/Scan
public object Getv_Update_ComplianceStatusAll()
{
var dato = DateTime.Now.AddYears(-1);
var scan = (from n in db.C_RES_COLL_NO9000AC
join s in db.v_Update_ComplianceStatusAll on n.MachineID equals s.ResourceID
join u in db.v_UpdateInfo on s.CI_ID equals u.CI_ID
join c in db.v_CICategoryInfo on u.CI_ID equals c.CI_ID
where (n.MachineID == s.ResourceID) && (u.DateRevised > dato)
group s by new { n.Name } into grp
select new
{
Name = grp.Key.Name,
StatusScan = grp.Max(t=> t.LastStatusCheckTime)
});
return scan;
}
This is my attemt at a fix outside the query :
var newScan = scan.ToList();
foreach (var s in newScan)
{
s.StatusScan = s.StatusScan.ToLocalTime();
}
return newScan;
The converstion works, but it returns "Error 306 Property or indexer 'AnonymousType#1.StatusScan' cannot be assigned to -- it is read only"
So, how do I convert the UTC to local time in the controller (before I return anything to the website)?
Yes, Anonymous Type is handy, but it is a bad idea to return it from a method -- Outsider do not know what actually the object is. It is recommended to create a strong type to store the result and return IEnumerable<ScanItem>. Then you are able to modify the result.
public class ScanItem
{
public string Name { get; set; }
public datetime StatusScan { get; set; }
}
If you must use Anonymous Type, You can build a new list like this
scan.ToList().Select(i => new {Name = i.Name, StatusScan = i.StatusScan.ToLocalTime()});
var newScan = scan.ToList();
foreach (var s in newScan)
{
s.StatusScan = s.StatusScan.ToLocalTime();
}
return newScan;
your newScan is a List, you cannot directly assign
s.StatusScan = s.StatusScan.ToLocalTime();
In sql server, I have two tables (lets say Person and School). I want to return the Person.Name from one table and School.Name (since they are related I want the primary key of the Person also).
So the question here is can I return these data and make them all in a list? also for the return type, does it request that I create a custom type for it (I mean class)?
Edit: Sorry I forget to metion that I need it in LINQ.
Thanks,
Maybe there is a better way to do this, but I would create a new class and return a list of these objects. Like this:
First do a LiNQ query to get the values we need:
var personWithSchoolName = select p from db.Person
select s from db.School
where p.SchoolId == s.SchoolId
select new { p.PersonId as PersonId, p.Name as PersonName, s.Name as SchoolName };
peopleWithSchoolNamesList = personWithSchoolName.ToList();
Then loop through the results and add these to a new list:
foreach(object o in peopleWithSchoolNamesList)
{
PersonWithSchoolNameObject personWithSchoolNameObject = new PersonWithSchoolNameObject(o.PersonId, o.PersonName, o.SchoolName);
ListWithPeople.Add(PersonWithSchoolNameObject);
}
return ListWithPeople;
You can use Join operation of LINQ to combine data from more than one table
For return purposes you will need a Concrete class. You cannot return the results of LINQ query i.e. an anonymous type, directly.
Try this
SELECT Person.Id, Person.Name, School.Name AS SchoolName
from Person
LEFT JOIN School ON Person.SchoolId = School.Id
(actually you have to use well prepared linq to return this, yeah?)
and wrap the results into special predefined class like:
class PersonWithSchool
{
public int PersonId;
public string PersonName;
public string SchoolName;
}
When you query existing linq results, it's like they're stuck a layer deeper than the original result. Let me explain what I mean by this.
In the example below, after getting ResultSorted, to get to the data therein, you have to use RowSorted.All.TableData.Field, but in the unsorted Result, you could just do Row.TableData.Field. In the sorted data, you have to use .All to get to the rest of the data, which is like an extra layer to get to the data you're looking for.
How can I get it so I can query Result without getting this extra layer? Thanks Stack-O!
var Result =
from a in Db.Table
select new {TableData = a};
var ResultSorted =
from a in Result
orderby a.TableData.Field
select new {All = a};
foreach(var RowSorted in ResultSorted)
{
MessageBox.Show(RowSorted.All.TableData.ToString());
}
You can use
var Result =
from a in Db.Table
select a;
var ResultSorted =
from a in Result
orderby a.Field
select a;
foreach(var RowSorted in ResultSorted)
{
MessageBox.Show(RowSorted.ToString());
}
Edit:
The thing is that
select new {TableData = a};
creates a new anonymous type with a field called TableData, like this
class Tmp1
{
TableType TableData {get; set;}
}
and
select new {All = a};
creates a new anonymous type with a field called TableData, like this
class Tmp2
{
Tmp1 All {get; set;}
}
Edit 2:
If you select a directly you don't create the extra anonymous type, instead you return the TableType.
You are returning a new instance of an anonymous type in each of your LINQ queries:
select new {TableData = a};
select new {All = a};
What you are saying to the compiler is (in the first LINQ query), "Give me a new instance of an anoymous type. I want this anonymous type to have one property named TableData and I want the value for that property to be a."
If you simply return a instead of an anoymous type, you shouldn't need to go through the properties of the nested types to get the data. Try this:
var Result =
from a in Db.Table
select a;
var ResultSorted =
from a in Result
orderby a.TableData.Field
select a;
foreach(var RowSorted in ResultSorted)
{
MessageBox.Show(RowSorted.ToString());
}
var ResultSorted =
from a in Db.Table
orderby a.Field
select a.ToString();
Edit: Fixed, didn't see the first query. This should be identical now. There is no need to create anonymous objects all the time.
Using the simple example below, what is the best way to return results from multiple tables using Linq to SQL?
Say I have two tables:
Dogs: Name, Age, BreedId
Breeds: BreedId, BreedName
I want to return all dogs with their BreedName. I should get all dogs using something like this with no problems:
public IQueryable<Dog> GetDogs()
{
var db = new DogDataContext(ConnectString);
var result = from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select d;
return result;
}
But if I want dogs with breeds and try this I have problems:
public IQueryable<Dog> GetDogsWithBreedNames()
{
var db = new DogDataContext(ConnectString);
var result = from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select new
{
Name = d.Name,
BreedName = b.BreedName
};
return result;
}
Now I realize that the compiler won't let me return a set of anonymous types since it's expecting Dogs, but is there a way to return this without having to create a custom type? Or do I have to create my own class for DogsWithBreedNames and specify that type in the select? Or is there another easier way?
I tend to go for this pattern:
public class DogWithBreed
{
public Dog Dog { get; set; }
public string BreedName { get; set; }
}
public IQueryable<DogWithBreed> GetDogsWithBreedNames()
{
var db = new DogDataContext(ConnectString);
var result = from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select new DogWithBreed()
{
Dog = d,
BreedName = b.BreedName
};
return result;
}
It means you have an extra class, but it's quick and easy to code, easily extensible, reusable and type-safe.
You can return anonymous types, but it really isn't pretty.
In this case I think it would be far better to create the appropriate type. If it's only going to be used from within the type containing the method, make it a nested type.
Personally I'd like C# to get "named anonymous types" - i.e. the same behaviour as anonymous types, but with names and property declarations, but that's it.
EDIT: Others are suggesting returning dogs, and then accessing the breed name via a property path etc. That's a perfectly reasonable approach, but IME it leads to situations where you've done a query in a particular way because of the data you want to use - and that meta-information is lost when you just return IEnumerable<Dog> - the query may be expecting you to use (say) Breed rather than Ownerdue to some load options etc, but if you forget that and start using other properties, your app may work but not as efficiently as you'd originally envisaged. Of course, I could be talking rubbish, or over-optimising, etc...
Just to add my two cents' worth :-)
I recently learned a way of handling anonymous objects. It can only be used when targeting the .NET 4 framework and that only when adding a reference to System.Web.dll but then it's quite simple:
...
using System.Web.Routing;
...
class Program
{
static void Main(string[] args)
{
object anonymous = CallMethodThatReturnsObjectOfAnonymousType();
//WHAT DO I DO WITH THIS?
//I know! I'll use a RouteValueDictionary from System.Web.dll
RouteValueDictionary rvd = new RouteValueDictionary(anonymous);
Console.WriteLine("Hello, my name is {0} and I am a {1}", rvd["Name"], rvd["Occupation"]);
}
private static object CallMethodThatReturnsObjectOfAnonymousType()
{
return new { Id = 1, Name = "Peter Perhac", Occupation = "Software Developer" };
}
}
In order to be able to add a reference to System.Web.dll you'll have to follow rushonerok's advice : Make sure your [project's] target framework is ".NET Framework 4" not ".NET Framework 4 Client Profile".
In C# 7 you can now use tuples!... which eliminates the need to create a class just to return the result.
Here is a sample code:
public List<(string Name, string BreedName)> GetDogsWithBreedNames()
{
var db = new DogDataContext(ConnectString);
var result = from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select new
{
Name = d.Name,
BreedName = b.BreedName
}.ToList();
return result.Select(r => (r.Name, r.BreedName)).ToList();
}
You might need to install System.ValueTuple nuget package though.
You must use ToList() method first to take rows from database and then select items as a class.
Try this:
public partial class Dog {
public string BreedName { get; set; }}
List<Dog> GetDogsWithBreedNames(){
var db = new DogDataContext(ConnectString);
var result = (from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select new
{
Name = d.Name,
BreedName = b.BreedName
}).ToList()
.Select(x=>
new Dog{
Name = x.Name,
BreedName = x.BreedName,
}).ToList();
return result;}
So, the trick is first ToList(). It is immediately makes the query and gets the data from database. Second trick is Selecting items and using object initializer to generate new objects with items loaded.
Hope this helps.
You could do something like this:
public System.Collections.IEnumerable GetDogsWithBreedNames()
{
var db = new DogDataContext(ConnectString);
var result = from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select new
{
Name = d.Name,
BreedName = b.BreedName
};
return result.ToList();
}
No you cannot return anonymous types without going through some trickery.
If you were not using C#, what you would be looking for (returning multiple data without a concrete type) is called a Tuple.
There are alot of C# tuple implementations, using the one shown here, your code would work like this.
public IEnumerable<Tuple<Dog,Breed>> GetDogsWithBreedNames()
{
var db = new DogDataContext(ConnectString);
var result = from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select new Tuple<Dog,Breed>(d, b);
return result;
}
And on the calling site:
void main() {
IEnumerable<Tuple<Dog,Breed>> dogs = GetDogsWithBreedNames();
foreach(Tuple<Dog,Breed> tdog in dogs)
{
Console.WriteLine("Dog {0} {1}", tdog.param1.Name, tdog.param2.BreedName);
}
}
Now I realize that the compiler won't let me return a set of anonymous types since it's expecting Dogs, but is there a way to return this without having to create a custom type?
Use use object to return a list of Anonymous types without creating a custom type.
This will work without the compiler error (in .net 4.0). I returned the list to the client and then parsed it on JavaScript:
public object GetDogsWithBreedNames()
{
var db = new DogDataContext(ConnectString);
var result = from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select new
{
Name = d.Name,
BreedName = b.BreedName
};
return result;
}
Just select dogs, then use dog.Breed.BreedName, this should work fine.
If you have a lot of dogs, use DataLoadOptions.LoadWith to reduce the number of db calls.
This doesn't exactly answer your question, but Google led me here based on the keywords. This is how you might query an anonymous type from a list:
var anon = model.MyType.Select(x => new { x.Item1, x.Item2});
You can not return anonymous types directly, but you can loop them through your generic method. So do most of LINQ extension methods. There is no magic in there, while it looks like it they would return anonymous types. If parameter is anonymous result can also be anonymous.
var result = Repeat(new { Name = "Foo Bar", Age = 100 }, 10);
private static IEnumerable<TResult> Repeat<TResult>(TResult element, int count)
{
for(int i=0; i<count; i++)
{
yield return element;
}
}
Below an example based on code from original question:
var result = GetDogsWithBreedNames((Name, BreedName) => new {Name, BreedName });
public static IQueryable<TResult> GetDogsWithBreedNames<TResult>(Func<object, object, TResult> creator)
{
var db = new DogDataContext(ConnectString);
var result = from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select creator(d.Name, b.BreedName);
return result;
}
Try this to get dynamic data. You can convert code for List<>
public object GetDogsWithBreedNames()
{
var db = new DogDataContext(ConnectString);
var result = from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select new
{
Name = d.Name,
BreedName = b.BreedName
};
return result.FirstOrDefault();
}
dynamic dogInfo=GetDogsWithBreedNames();
var name = dogInfo.GetType().GetProperty("Name").GetValue(dogInfo, null);
var breedName = dogInfo.GetType().GetProperty("BreedName").GetValue(dogInfo, null);
BreedId in the Dog table is obviously a foreign key to the corresponding row in the Breed table. If you've got your database set up properly, LINQ to SQL should automatically create an association between the two tables. The resulting Dog class will have a Breed property, and the Breed class should have a Dogs collection. Setting it up this way, you can still return IEnumerable<Dog>, which is an object that includes the breed property. The only caveat is that you need to preload the breed object along with dog objects in the query so they can be accessed after the data context has been disposed, and (as another poster has suggested) execute a method on the collection that will cause the query to be performed immediately (ToArray in this case):
public IEnumerable<Dog> GetDogs()
{
using (var db = new DogDataContext(ConnectString))
{
db.LoadOptions.LoadWith<Dog>(i => i.Breed);
return db.Dogs.ToArray();
}
}
It is then trivial to access the breed for each dog:
foreach (var dog in GetDogs())
{
Console.WriteLine("Dog's Name: {0}", dog.Name);
Console.WriteLine("Dog's Breed: {0}", dog.Breed.Name);
}
If the main idea is to make the SQL select statement sent to the Database server have only the required fields, and not all the Entity fields, then u can do this:
public class Class1
{
public IList<Car> getCarsByProjectionOnSmallNumberOfProperties()
{
try
{
//Get the SQL Context:
CompanyPossessionsDAL.POCOContext.CompanyPossessionsContext dbContext
= new CompanyPossessionsDAL.POCOContext.CompanyPossessionsContext();
//Specify the Context of your main entity e.g. Car:
var oDBQuery = dbContext.Set<Car>();
//Project on some of its fields, so the created select statment that is
// sent to the database server, will have only the required fields By making a new anonymouse type
var queryProjectedOnSmallSetOfProperties
= from x in oDBQuery
select new
{
x.carNo,
x.eName,
x.aName
};
//Convert the anonymouse type back to the main entity e.g. Car
var queryConvertAnonymousToOriginal
= from x in queryProjectedOnSmallSetOfProperties
select new Car
{
carNo = x.carNo,
eName = x.eName,
aName = x.aName
};
//return the IList<Car> that is wanted
var lst = queryConvertAnonymousToOriginal.ToList();
return lst;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
throw;
}
}
}
If you have a relationship setup in your database with a foriegn key restraint on BreedId don't you get that already?
So I can now call:
internal Album GetAlbum(int albumId)
{
return Albums.SingleOrDefault(a => a.AlbumID == albumId);
}
And in the code that calls that:
var album = GetAlbum(1);
foreach (Photo photo in album.Photos)
{
[...]
}
So in your instance you'd be calling something like dog.Breed.BreedName - as I said, this relies on your database being set up with these relationships.
As others have mentioned, the DataLoadOptions will help reduce the database calls if that's an issue.
Well, if you're returning Dogs, you'd do:
public IQueryable<Dog> GetDogsWithBreedNames()
{
var db = new DogDataContext(ConnectString);
return from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select d;
}
If you want the Breed eager-loaded and not lazy-loaded, just use the appropriate DataLoadOptions construct.