What seems to be an easy task in C# doesn't seem so easy in F#...
Given the following types in C# I want to make an inner join with F#:
public partial class Foo
{
public long FooID { get; set; }
public long BarID { get; set; }
public bool SomeColumn1 { get; set; }
}
public partial class Bar
{
public long ID { get; set; }
public string SomeColumn1 { get; set; }
public bool SomeColumn2 { get; set; }
}
So my try at doing this is:
let dbContext = DatabaseManager.Instance.ProduceContext()
let barIdForeignKey(f: Foo) =
f.BarID
let barIdPrimaryKey(b: Bar) =
b.ID
let joinResult(f: Foo, b: Bar) =
(f, b)
let joinedElements =
dbContext.foos.Join(dbContext.bars, barIdForeignKey, barIdPrimaryKey, joinResult)
But the compiler complains with something like:
Possible overload: (extension)
System.Collections.Generic.IEnumerable.Join<'TOuter, 'TInner, 'TKey, 'TResult>(
inner: System.Collections.Generic.IEnumerable<'TInner>,
outerKeySelector: System.Func<'TOuter, 'TKey>,
innerKeySelector: System.Func<'TInner, 'TKey>,
resultSelector: System.Func<'TOuter, 'TInner, 'TResult>)
: System.Collections.Generic.IEnumerable<'TResult>
Type constraint mismatch. The type 'd * 'e -> foo * bar is not compatible with type System.Func<'a, 'b, 'c>
The type 'd * 'e -> foo * bar is not compatible with the type System.Func<'a, 'b, 'c>
Not sure how to read this. Maybe it's that I cannot return a tuple at the end? In C# I would need an anonymous type, like new { Foo = foo, Bar = bar }, not sure how to do that in F#.
Solution (thanks #ildjarn) using currified arguments for the last func:
let dbContext = DatabaseManager.Instance.ProduceContext()
let barIdForeignKey(f: Foo) =
f.BarID
let barIdPrimaryKey(b: Bar) =
b.ID
let joinResult(f: Foo) (b: Bar) =
(f, b)
let joinedElements =
dbContext.foos.Join(dbContext.bars, barIdForeignKey, barIdPrimaryKey, joinResult)
Which in the end can be simplified to:
let joinedElements =
dbContext.foos.Join (dbContext.bars,
(fun f -> f.barID),
(fun b -> b.ID),
(fun f b -> (f,b))
)
#PanagiotisKanavos also gave me a hint about joins using an ORM being a code-smell, and that let me discover that there was actually a Bar property in the Foo class (so that I don't need to fiddle with BarID column, as this Bar property is populated under the hood by EntityFramework this way anyway). This, combined with #ildjarn's original suggestion for using query expressions, led me to the best answer:
let dbContext = DatabaseManager.Instance.ProduceContext()
let joinedElements =
query {
for foo in dbContext.foos do
select (foo.Bar, foo.SomeColumn1)
}
Related
The problem simplified is as follows: in Entity Framework i am doing a join involving 3 tables, and returning the joined result set, which involves (some) fields from the 3 tables.
var query = (
from t1 in dbCtx.TB_Entity1
from t2 in dbCtx.TB_Entity2
.Where(p => p.someCol == t1.someCol && t.IsActive == true)
.DefaultIfEmpty() //LEFT JOIN
from t3 in dbCtx.TB_Entity3
.Where(q => q.someCol == t2.someCol)
where t1.IsLatest == true
&& (t1.istatus == 2
|| t1.istatus == 3
)
select new {
t1.col100,
t1.col101,
t2.col200,
t2.col201,
t3.col300,
t3.col301
}).OrderByDescending(t1 => t1.ID);
var anonObjList = query.ToList();
Thus, at the end of the query I write a projection to select the fields i want.
Finally i run the query with .ToList() and get a list of Anonymous objects.
How do i modify the query to project into a List of MyConcreteClass
i.e. i want to be able to write something similar to
List<MyConcreteClass> myObjList = query.ToList();
You may assume my concrete class looks like
public class MyConcreteClass
{
public string Col100 { get; set; }
public string Col101 { get; set; }
public string Col200 { get; set; }
public string Col201 { get; set; }
public string Col300 { get; set; }
public string Col301 { get; set; }
}
You just use the object initializer syntax:
new MyConcreteClass
{
Col100 = t1.col100,
Col101 = t1.col101,
Col200 = t2.col200,
Col201 = t2.col201,
Col300 = t3.col300,
Col301 = t3.col301
}
I'm trying to find out how I would define the code first navigation properties on these two classes to perform something similiar to this query:
SELECT USERID, FIRSTNAME, LASTNAME, COURSEID, NAME
FROM User
LEFT OUTER JOIN Course ON User.USERID = Course.USERID
WHERE COURSEID = 1
So I'm trying to find a list of users together with if they have attended a certain course.
public class User
{
public int UserId {get;set; }
public string FirstName {get;set;}
public string LastName {get;set;}
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public int CourseId { get;set; }
public int UserId { get;set; }
public string Name { get;set; }
public virtual User User {get;set;}
}
If I was to write a query to achieve this
var u = Users.Where(x => x.Courses.Any(x => x.CourseId = 1));
This does a subquery, which is not what I wanted (as people who didnt attend the course would not show).
How would the navigation property be defined?
HasMany(t => t.Courses).WithOptional(t => t.User).HasForeignKey(t => t.UserId);
Check this link:
http://msdn.microsoft.com/en-us/library/vstudio/bb397895.aspx
Left outer joins in LINQ are done via DefaultIfEmpty method.
var u = Users.Select ( x => new {
User = x,
AttendedCourse = x.Courses.Any()
} );
For specific course id,
var u = Users.Select ( x => new {
User = x,
AttendedCourse = x.Courses.Any( c => c.CourseID == 1 )
} );
Sub query is the only way to write related queries, However, EF will choose the best suitable join type and will give you correct results. And EF can manage mostly all kinds of queries without doing joins.
After I found out that navigation properties does not populate after insert operation in EF Code first , I decide to create a method to reload the navigations. Lets think we have two classes :
public class Book
{
public int Id {get ; set;}
public string Title {get ; set;}
public virtual ICollection<Page> Pages {get ; set;}
}
public class Page
{
public int Id {get ; set;}
public string Content {get ; set;}
public int BookId {get ; set;}
public virtual Book Book {get ; set;}
}
and in the following code I have :
DbContext db = new DbContext()
Page p = new Page();
p.BookId = 1;
p.Content = "Sample Content";
db.Pages.Add(p);
db.SaveChanges();
db.Reload(p, rec => rec.Book); // Here is the wanted method
I tried this one , but I failed
public class DbContext : DbContext
{
///I know this method is wrong, I have no idea to fix it
public T Reload<T>(T Obj, param Func<T,I_DONT_KNOW_WHAT_I_SHOULD_PUT_HERE>[] predicates )
{
foreach(var item in predicates)
{
this.Entry<T>(Obj).Reference(item).Load();
}
}
}
Is there anybody out there to help me complete this method ?
Im note sure I follow what you are trying to do.
But my first though is to recommend
The basic reload form is:
Context.Entry<TPoco>(poco).Reload();
You can also use Include form
var Qpocolist = Context.Set<TPoco>().Include(t=>t.Navprop).Where(t => t.Navprop.xyz=="xyz");
but perhaps it is just passing an array of prop expression you wanted a tip on.
You need to declare the method as generic for Poco and Property.
public void MyFancySetNavs<TPoco, TProp>(TPoco obj, params Expression<Func<TPoco, TProp>>[] navProps) {
foreach (var navPropExp in navProps) {
// do ya thing...
}
}
I change my code to this , so the navigators populate correctly. So I do not need such mentioned method
DbContext db = new DbContext()
Page p = db.Pages.Create();
p.BookId = 1;
p.Content = "Sample Content";
db.Pages.Add(p);
db.SaveChanges();
db.Reload(p, rec => rec.Book);
I would like to ask what is "var" in this statement.
var context = new MHC_CoopEntities();
var lists = (from c in context.InventLists
select new
{
c.InventID,
c.ItemName,
c.InventCategory.CategoryID,
c.UnitQty,
c.UnitPrice
}).ToList();
ListGridView.DataSource = lists;
ListGridView.DataBind();
I know that "var" can be any value. I am trying to create a helper class for this.
var has nothing to do with Entity Framework. It's a pure C# construct allowing you to define an implicitly typed object. It's explained in the documentation. Basically it allows the compiler to infer the actual type of the variable from the right handside of the assignment. This avoids you repeating the same type declaration twice. It is also necessary for anonymous types which do not have a name. For example:
var foo = new { Id = 123, Name = "anon" };
And this is exactly what happens in your example. In the select clause you are returning an anonymous type. So the only way is to use var.
In the first example:
var context = new MHC_CoopEntities();
it is equivalent to:
MHC_CoopEntities context = new MHC_CoopEntities();
because we know the type.
It will be a new anonymous type. If you need to pass it between functions, you should declare the custom type first (I've called it InventType):
public class InventType {
int InventId { set; get; }
string ItemName { set; get; }
int CategoryId { set; get; }
int UnitQty { set; get; }
decimal UnitPrice { set; get; }
}
var lists = (from c in context.InventLists
select new InventType
{
InventId = c.InventID,
ItemName = c.ItemName,
CategoryId = c.InventCategory.CategoryID,
UnitQty = c.UnitQty,
UnitPrice = c.UnitPrice
}).ToList();
Now var represents List<InventType>.
I have read all the posts related to implementing the equivalent of a LEFT OUTER JOIN in Linq to Entities (.NET 3.5) when using the Entity Framework, but have yet to find a solution to the following problem.
Given two tables, represented by the objects below:
public class Foo
{
public int FooId; // primary key
public string Name;
}
public class Bar
{
public int BarId; // primary key
public int FooId; // foreign key
public string Desc;
}
I need to create a Linq to Entities statement which is the EQUIVALENT of the following SQL statement. Note that the WHERE statement contains two OR'd conditions which span both tables, and the use of the DISTINCT qualifier.
SELECT DISTINCT
Foo.*
FROM
Foo
LEFT OUTER JOIN Bar ON Foo.FooId = Bar.FooId
WHERE
(Foo.Name = 'fooname' OR Bar.Desc = 'bardesc')
The Linq query that I am generating is Linq to Entities via the Entity Framework, and will (hopefully) generate a single SQL statement to be executed on the server. Linq to Entities does not support the FirstOrDefault() extension clause, so the standard Linq syntax for LEFT OUTER JOINs will not work.
Here is the solution that I have SO FAR, but I am unable to do either of the following:
1) Generate a result set which contains the set of Foo/Bar combinations that would be returned by the LEFT OUTER JOIN operation.
2) Implement the equivalent of the WHERE clause: WHERE (Foo.Name = 'fooname' OR Bar.Desc = 'bardesc')
private class JoinSet
{
public Foo Foo;
public IQueryable<Bar> Bars;
};
private class FooBar
{
public Foo Foo;
public Bar Bar;
};
IEnumerable<Foo> OuterJoinTest()
{
IQueryable<Foo> fooBaseQuery = dbContext.FooSet;
IQueryable<Bar> barBaseQuery = dbDontext.BarSet;
IQueryable<JoinSet> joinQuery =
from foo in fooBaseQuery
select new JoinSet
{
Foo = foo,
Bars = barBaseQuery.Where(bar => bar.FooId == foo.FooId)
};
// How do I generate a result set containing FooBar objects ?
// How or where do I insert the equivalent of: ?
// WHERE (Foo.Name = 'fooname' OR Bar.Description = 'bardesc')
IQueryable<Foo> resultQuery =
from joinSet in joinQuery
select joinSet.Foo;
resultQuery = resultQuery.Distinct();
return resultQuery.ToList();
}
Any help, ideas or suggestions would be appreciated.
EulerOperator
.NET 3.5
private class FooBar
{
public Foo Foo { get; set; }
public Bar? Bar { get; set; }
}
var innerQuery = from foo in context.Foos
from bar in context.Bars
where foo.Name == 'fooname' || bar.Description == 'bardesc'
where foo.FooId == bar.FooId
select new FooBar { Foo = foo, Bar = bar };
var outerQuery = from foo in context.Foos
where foo.Name == 'fooname'
where !context.Bars.Any(b => b.FooId == foo.FooId)
select new FooBar { Foo = foo, Bar = null };
var leftouterjoinQuery = innerQuery.Union(outerQuery).Distinct();
.NET 4.0
var query = (from foo in context.Foo
join b in context.Bar
on foo.FooId equals b.FooId into Bar
from bar in Bar.DefaultIfEmpty()
where foo.Name = 'fooname' || bar.Description = 'bardesc'
select new { foo, bar }).Distinct();