LINQ selectors inheritance - c#

It's not impossible that my question has already been answered here, but unfortunately when it comes to LINQ I can't always find the right search terms.
Given the underlying source code how do you think I can reuse Selectors.CustomerDTO selector within Selectors.CustomerExtendedDTO?
It should be possible since the input database entity is the same.
class Program
{
static void Main(string[] args)
{
using (NorthwindEntities ctx = new NorthwindEntities())
{
foreach (var item in ctx.Customers.Where(c => c.CompanyName.StartsWith("A")).Select(Selectors.CustomerDTO))
{
Console.WriteLine("CompanyName: {0}", item.CompanyName);
Console.WriteLine();
}
foreach (var item in ctx.Customers.Where(c => c.CompanyName.StartsWith("A")).Select(Selectors.CustomerExtendedDTO))
{
Console.WriteLine("CompanyName: {0}", item.CompanyName);
Console.WriteLine("ContactName: {0}", item.ContactName);
Console.WriteLine();
}
Console.ReadLine();
}
}
}
class CustomerDTO
{
public string CustomerID { get; set; }
public string CompanyName { get; set; }
}
class CustomerExtendedDTO : CustomerDTO
{
public string ContactName { get; set; }
public string Address { get; set; }
}
static class Selectors
{
internal static Expression<Func<Customers, CustomerDTO>> CustomerDTO
{
get
{
return c => new CustomerDTO
{
CustomerID = c.CustomerID,
CompanyName = c.CompanyName
};
}
}
internal static Expression<Func<Customers, CustomerExtendedDTO>> CustomerExtendedDTO
{
get
{
return c => new CustomerExtendedDTO
{
CustomerID = c.CustomerID,
CompanyName = c.CompanyName,
Address = c.Address,
ContactName = c.ContactName
};
}
}
}
Such thing would be very useful when the inherited projection entity does not only contain two properties but several dozens.
Thanks for any ideas.

Related

How to change the following foreach code to Linq Query?

public class A
{
public List<Data> data { get; set; }
}
public class Data
{
public int ID { get; set; }
public int values { get; set; }
}
public class B
{
public List<DifferentData> Data { get; set; }
}
public class DifferentData
{
public int NewID { get; set; }
public int Differentvalues { get; set; }
public string AdditionalInfo { get; set; }
}
I have a code like below
var TopAClassData = new A();
var TopBClassData = new B();
var anotherItemD = new DifferentData();
foreach (var item in TopAClassData.data)
{
foreach (var anotherItem in TopBClassData.Data)
{
if (item.ID == anotherItem.NewID)
{
item.values = anotherItemD.Differentvalues;
}
}
}
I'm trying to set the Differentvalues from the List to values field of List if the NewId and ID are matching. Is there anyway to convert the foreach code to simpler linq query
You can join two lists and then enumerate:
var items = TopAClassData.data
.Join(TopBClassData.Data, x => x.ID, x => x.NewId,
(item, anotherItem) => item);
foreach(var item in items)
{
item.values = anotherItemD.Differentvalues;
}
Here's a fairly nice LINQ version:
IEnumerable<Data> items =
from item in TopAClassData.data
join anotherItem in TopBClassData.Data on item.ID equals anotherItem.NewID
select item;
foreach (Data item in items)
item.values = anotherItemD.Differentvalues;

Dynamic LINQ Query System.Linq.Dynamic.Core

System.Linq.Dynamic.Core.Exceptions.ParseException: 'No property or field 'Name' exists in type 'IEnumerable`1''
How to use Complex object to do order by in Dynamic linq query! I am using System.Linq.Dynamic.Core for dynamic linq query.
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<Student> Students { get; set; }
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public Course Course { get; set; }
}
using (var context = new SchoolContext())
{
var list = context.Courses.Include("Students").AsQueryable();
var result = list.OrderBy("Students.Name").ToList();
foreach (var item in list)
{
Console.WriteLine("Hello World!" + item.Name );
}
}
The issue you are running into is trying to run an order by on a list.
If you want the students ordered by name in a course try the following
var list = context.Courses.Include(c => c.Students).AsQueryable();
var result = list.Select(course =>
new Course {
Id = course.Id,
Name = course.Name,
Students = course.Students.OrderBy(s => s.Name)
}).ToList();
foreach (var item in list)
{
foreach(var student in item.Students)
{
Console.WriteLine("Hello World!" + student.Name );
}
}

How to do Cascading Include in LiteDB

here is example on how to store cross-referenced entities in LiteDB. LiteDB stores the cross-referenced entities perfectly fine, but problem comes when I am trying to find/load entities back. My goal is NOT ONLY the requested entity but also referenced ones. There is quick tutorial section "DbRef for cross references" on LiteDB webpage how one can realize it. LiteDB has "Include" option (which is called before "FindAll") which says which referenced entities must be loaded as well. I am trying to achieve it in this code example but with no results, i.e, the code raises Exception("D_Ref") meaning "D_Ref" reference is not loaded:
namespace _01_simple {
using System;
using LiteDB;
public class A {
public int Id { set; get; }
public string Name { set; get; }
public B B_Ref { set; get; }
}
public class B {
public int Id { set; get; }
public string Name { set; get; }
public C C_Ref { set; get; }
}
public class C {
public int Id { set; get; }
public string Name { set; get; }
public D D_Ref { set; get; }
}
public class D {
public int Id { set; get; }
public string Name { set; get; }
}
class Program {
static void Main(string[] args) {
test_01();
}
static string NameInDb<T>() {
var name = typeof(T).Name + "s";
return name;
}
static void test_01() {
if (System.IO.File.Exists(#"MyData.db"))
System.IO.File.Delete(#"MyData.db");
using (var db = new LiteDatabase(#"MyData.db")) {
var As = db.GetCollection<A>(NameInDb<A>());
var Bs = db.GetCollection<B>(NameInDb<B>());
var Cs = db.GetCollection<C>(NameInDb<C>());
var Ds = db.GetCollection<D>(NameInDb<D>());
LiteDB.BsonMapper.Global.Entity<A>().DbRef(x => x.B_Ref, NameInDb<B>());
LiteDB.BsonMapper.Global.Entity<B>().DbRef(x => x.C_Ref, NameInDb<C>());
LiteDB.BsonMapper.Global.Entity<C>().DbRef(x => x.D_Ref, NameInDb<D>());
var d = new D { Name = "I am D." };
var c = new C { Name = "I am C.", D_Ref = d };
var b = new B { Name = "I am B.", C_Ref = c };
var a = new A { Name = "I am A.", B_Ref = b };
Ds.Insert(d);
Cs.Insert(c);
Bs.Insert(b);
As.Insert(a);
}
using (var db = new LiteDatabase(#"MyData.db")) {
var As = db.GetCollection<A>(NameInDb<A>());
var all_a = As
.Include(x => x.B_Ref)
.FindAll();
foreach (var a in all_a) {
if (a.B_Ref == null)
throw new Exception("B_Ref");
if (a.B_Ref.C_Ref == null)
throw new Exception("C_Ref");
if (a.B_Ref.C_Ref.D_Ref == null)
throw new Exception("D_Ref");
}
}
}
}}
after small research I've resolved the issue simply by adding extra "Include" parameterize by "x => x.B_Ref.C_Ref" lambda where x.B_Ref.C_Ref is a path in hierarchy of references:
var all_a = As
.Include(x => x.B_Ref)
.Include(x => x.B_Ref.C_Ref)
.FindAll();
Here is complete example
namespace _01_simple {
using System;
using LiteDB;
public class A {
public int Id { set; get; }
public string Name { set; get; }
public B B_Ref { set; get; }
}
public class B {
public int Id { set; get; }
public string Name { set; get; }
public C C_Ref { set; get; }
}
public class C {
public int Id { set; get; }
public string Name { set; get; }
public D D_Ref { set; get; }
}
public class D {
public int Id { set; get; }
public string Name { set; get; }
}
class Program {
static void Main(string[] args) {
test_01();
}
static string NameInDb<T>() {
var name = typeof(T).Name + "s";
return name;
}
static void test_01() {
if (System.IO.File.Exists(#"MyData.db"))
System.IO.File.Delete(#"MyData.db");
using (var db = new LiteDatabase(#"MyData.db")) {
var As = db.GetCollection<A>(NameInDb<A>());
var Bs = db.GetCollection<B>(NameInDb<B>());
var Cs = db.GetCollection<C>(NameInDb<C>());
var Ds = db.GetCollection<D>(NameInDb<D>());
LiteDB.BsonMapper.Global.Entity<A>().DbRef(x => x.B_Ref, NameInDb<B>());
LiteDB.BsonMapper.Global.Entity<B>().DbRef(x => x.C_Ref, NameInDb<C>());
LiteDB.BsonMapper.Global.Entity<C>().DbRef(x => x.D_Ref, NameInDb<D>());
var d = new D { Name = "I am D." };
var c = new C { Name = "I am C.", D_Ref = d };
var b = new B { Name = "I am B.", C_Ref = c };
var a = new A { Name = "I am A.", B_Ref = b };
Ds.Insert(d);
Cs.Insert(c);
Bs.Insert(b);
As.Insert(a);
}
using (var db = new LiteDatabase(#"MyData.db")) {
var As = db.GetCollection<A>(NameInDb<A>());
var all_a = As
.Include(x => x.B_Ref)
.Include(x => x.B_Ref.C_Ref)
.Include(x => x.B_Ref.C_Ref.D_Ref)
.FindAll();
foreach (var a in all_a) {
if (a.B_Ref == null)
throw new Exception("B_Ref");
if (a.B_Ref.C_Ref == null)
throw new Exception("C_Ref");
if (a.B_Ref.C_Ref.D_Ref == null)
throw new Exception("D_Ref");
}
}
}
}}
I hope it saves someone's time.
Update: LiteDB author says there is no support for Cascading Include. But it is planned in the next version (see issue). Consider, once, let say, B_Ref is a Lite of B, then there is no mechanism to force deeper Include.

How to copy a List<> to another List<> with Comparsion in c#

I an having Two Lists. I want to get the matched and unmatched values based on ID and add the results to another List. I can get both of these using Intersect/Except.
But I can get only ID in the resultant variables (matches and unmatches) . I need all the properties in the Template.
List<Template> listForTemplate = new List<Template>();
List<Template1> listForTemplate1 = new List<Template1>();
var matches = listForTemplate .Select(f => f.ID)
.Intersect(listForTemplate1 .Select(b => b.ID));
var ummatches = listForTemplate .Select(f => f.ID)
.Except(listForTemplate1.Select(b => b.ID));
public class Template
{
public string ID{ get; set; }
public string Name{ get; set; }
public string Age{ get; set; }
public string Place{ get; set; }
public string City{ get; set; }
public string State{ get; set; }
public string Country{ get; set; }
}
public class Template1
{
public string ID{ get; set; }
}
If you don't want to implement IEquality for this simple task, you can just modify your LINQ queries:
var matches = listForTemplate.Where(f => listForTemplate1.Any(b => b.ID == f.ID));
and
var unmatches = listForTemplate.Where(f => listForTemplate1.All(b => b.ID != f.ID));
You might want to check for null before accessing ID, but it should work.
You are looking for the overloaded function, with the second parameter IEqualityComparer. So make your comparer ( example: http://www.blackwasp.co.uk/IEqualityComparer.aspx ), and use the same comparer in intersect / except.
And for the generic part: maybe you should have a common interface for templates e.g. ObjectWithID describing that the class have a string ID property. Or simply use dynamic in your comparer (but I think this is very-very antipattern because you can have run time errors if using for the bad type).
You also have a problem: intersecting two collections with two different types will result in a collection of Object (common parent class). Then you have to cast a lot (antipattern). I advise you to make a common abstract class/interface for your template classes, and it is working. If you need to cast the elements back, do not cast, but use the visitior pattern: http://en.wikipedia.org/wiki/Visitor_pattern
Example (good):
static void Main(string[] args)
{
// http://stackoverflow.com/questions/16496998/how-to-copy-a-list-to-another-list-with-comparsion-in-c-sharp
List<Template> listForTemplate = new Template[] {
new Template(){ID = "1"},
new Template(){ID = "2"},
new Template(){ID = "3"},
new Template(){ID = "4"},
new Template(){ID = "5"},
new Template(){ID = "6"},
}.ToList();
List<Template1> listForTemplate1 = new Template1[] {
new Template1(){ID = "1"},
new Template1(){ID = "3"},
new Template1(){ID = "5"}
}.ToList();
var comp = new ObjectWithIDComparer();
var matches = listForTemplate.Intersect(listForTemplate1, comp);
var ummatches = listForTemplate.Except(listForTemplate1, comp);
Console.WriteLine("Matches:");
foreach (var item in matches) // note that item is instance of ObjectWithID
{
Console.WriteLine("{0}", item.ID);
}
Console.WriteLine();
Console.WriteLine("Ummatches:");
foreach (var item in ummatches) // note that item is instance of ObjectWithID
{
Console.WriteLine("{0}", item.ID);
}
Console.WriteLine();
}
}
public class ObjectWithIDComparer : IEqualityComparer<ObjectWithID>
{
public bool Equals(ObjectWithID x, ObjectWithID y)
{
return x.ID == y.ID;
}
public int GetHashCode(ObjectWithID obj)
{
return obj.ID.GetHashCode();
}
}
public interface ObjectWithID {
string ID { get; set; }
}
public class Template : ObjectWithID
{
public string ID { get; set; }
public string Name { get; set; }
public string Age { get; set; }
public string Place { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
public class Template1 : ObjectWithID
{
public string ID { get; set; }
}
Output:
Matches:
1
3
5
Ummatches:
2
4
6
Press any key to continue . . .
For comparison, this should also work (the first part is a variation on #MAV's answer):
var matches = from item in listForTemplate
join id in listForTemplate1 on item.ID equals id.ID
select item;
var unmatches = listForTemplate.Where(item => matches.All(elem => elem.ID != item.ID));
matches and unmatches will both be IEnumerable<Template> which is the type you require.
However, MAV's answer works fine so I'd go for that one.
As mentioned, Implement the IEqualityComparer<T> interface.
IEqualityComparer<T> MSDN
Then use this as an argument in your method for Except() and Intersect()
Intersect
There is a good example of how to do so on the link for the Intersect() method.
If you don't absolutely have to use LINQ, why not code something like this?
var matches = new List<Template>();
var unmatches = new List<Template>();
foreach (var entry in listForTemplate)
{
bool matched = false;
foreach (var t1Entry in listForTemplate1)
{
if (entry.ID == t1Entry.ID)
{
matches.Add(entry);
matched = true;
break;
}
}
if (!matched)
{
unmatches.Add(entry);
}
}
A disadvantage of the LINQ approach is that you're traversing the lists twice.

RavenDb how do I reduce group values into collection in reduce final result?

I hope it's more clear what I want to do from the code than the title. Basically I am grouping by 2 fields and want to reduce the results into a collection all the ProductKey's constructed in the Map phase.
public class BlockResult
{
public Client.Names ClientName;
public string Block;
public IEnumerable<ProductKey> ProductKeys;
}
public Block()
{
Map = products =>
from product in products
where product.Details.Block != null
select new
{
product.ClientName,
product.Details.Block,
ProductKeys = new List<ProductKey>(new ProductKey[]{
new ProductKey{
Id = product.Id,
Url = product.Url
}
})
};
Reduce = results =>
from result in results
group result by new {result.ClientName, result.Block} into g
select new BlockResult
{
ClientName = g.Key.ClientName,
Block = g.Key.Block,
ProductKeys = g.SelectMany(x=> x.ProductKeys)
};
}
I get some weird System.InvalidOperationException and a source code dump where basically it is trying to initialize the list with an int (?).
If I try replacing the ProductKey with just IEnumerable ProductIds (and make appropriate changes in the code). Then the code runs but I don't get any results in the reduce.
You probably don't want to do this. Are you really going to need to query in this manner? If you know the context, then you should probably just do this:
var q = session.Query<Product>()
.Where(x => x.ClientName == "Joe" && x.Details.Block == "A");
But, to answer your original question, the following index will work:
public class Products_GroupedByClientNameAndBlock : AbstractIndexCreationTask<Product, Products_GroupedByClientNameAndBlock.Result>
{
public class Result
{
public string ClientName { get; set; }
public string Block { get; set; }
public IList<ProductKey> ProductKeys { get; set; }
}
public class ProductKey
{
public string Id { get; set; }
public string Url { get; set; }
}
public Products_GroupedByClientNameAndBlock()
{
Map = products =>
from product in products
where product.Details.Block != null
select new {
product.ClientName,
product.Details.Block,
ProductKeys = new[] { new { product.Id, product.Url } }
};
Reduce = results =>
from result in results
group result by new { result.ClientName, result.Block }
into g
select new {
g.Key.ClientName,
g.Key.Block,
ProductKeys = g.SelectMany(x => x.ProductKeys)
};
}
}
When replicating I get the same InvalidOperationException, stating that it doesn't understand the index definition (stack trace omitted for brevity).
Url: "/indexes/Keys/ByNameAndBlock"
System.InvalidOperationException: Could not understand query:
I'm still not entirely sure what you're attempting here, so this may not be quite what you're after, but I managed to get the following working. In short, Map/Reduce deals in anonymous objects, so strongly typing to your custom types makes no sense to Raven.
public class Keys_ByNameAndBlock : AbstractIndexCreationTask<Product, BlockResult>
{
public Keys_ByNameAndBlock()
{
Map = products =>
from product in products
where product.Block != null
select new
{
product.Name,
product.Block,
ProductIds = product.ProductKeys.Select(x => x.Id)
};
Reduce = results =>
from result in results
group result by new {result.Name, result.Block}
into g
select new
{
g.Key.Name,
g.Key.Block,
ProductIds = g.SelectMany(x => x.ProductIds)
};
}
}
public class Product
{
public Product()
{
ProductKeys = new List<ProductKey>();
}
public int ProductId { get; set; }
public string Url { get; set; }
public string Name { get; set; }
public string Block { get; set; }
public IEnumerable<ProductKey> ProductKeys { get; set; }
}
public class ProductKey
{
public int Id { get; set; }
public string Url { get; set; }
}
public class BlockResult
{
public string Name { get; set; }
public string Block { get; set; }
public int[] ProductIds { get; set; }
}

Categories

Resources