Creating LINQ with where clause - c#

I have this LINQ:
private static object getObjectModels(DbContext context, IQueryable<int> contractsID)
{
return (from objectModel in context.Set<ObjectModel>()
where "column named conId contains contractsID "
select new ContractHelper
{
Id = contract.Id,
ClientId = contract.ClientId,
});
}
I need to select from table records where column named conID have values of contractsID.
The contractsID is int array.
The conID is int value column.
What have I write in this row:
where "column named conId contains contractsID"
to get all records where column conID have item that equal to item in contractsID array?

You might be able to invert the where clause and use a 'contains', such as:
private static object getObjectModels(DbContext context, IQueryable<int> contractsID)
{
return (from objectModel in context.Set<ObjectModel>()
where objectModel.conId.HasValue && contractsID.Contains(objectModel.conId)
select new ContractHelper
{
Id = contract.Id,
ClientId = contract.ClientId,
});
}
You might need to convert the IQueryable to a list however.
var myIds = contractIDs.ToList();
...
where myIds.Contains(objectModel.conId)
...

You can go with an int array to make linq translate to the correct IN SQL syntax
private static object getObjectModels(DbContext context, IQueryable<int> contractsID)
{
// Necessary to translate Contains to SQL IN CLAUSE
int [] contractIdsArray = contractsID.ToArray() ;
return (from objectModel in context.Set<ObjectModel>()
where contractIdsArray.Contains(objectModel.conId)
select new ContractHelper
{
Id = contract.Id,
ClientId = contract.ClientId,
});
}

Related

LINQ get find all with id in list

I am trying to figure out non query way to do return a list of all objects if their ID is in test list. Example below:
Hero - table
Columns: id = INT , name = STRING, age = INT, power = INT;
var testList = {1,2,3};
var secondArray = {};
foreach (var id in testList )
{
// check if ID in database
var item = db.Hero.ToList().Find(o => o.Id = id);
if( item != null)
{
secondArray.push(item);
}
}
Now i have seen this whole thing done in single line but cannot remember how it was done.
The result i am after is List of all objects containing that have ids 1,2,3.
You have to use Contains on testList:
var secondArray= db.Hero.Where (h=> testList.Contains(h.Id))
How about
var result = db.Hero.Where(x => testList.Contains(x.Id));
This would hit DB just once instead of 3 times.

C# lambda - Differences (Except) of inner lists inside two lists

I have two lists of 'Table' 1 ----N 'Columns.
The first list holds the default schema that must be achieved.
The second list holds the schema defined by the user.
I need to compare the second list against the first one, retrieving the tables where the schema mismatches, also the list of columns missing/unknown.
Consider the following example:
public class Table
{
public string Name {get;set;}
public IList<Column> Columns {get;set;}
public Table()
{
Columns = new List<Column>();
}
}
public class Column
{
public string Name {get;set;}
}
//...
var Default1 = new Table() { Name = "Table1" };
Default1.Columns.Add(new Column() { Name = "X1" });
Default1.Columns.Add(new Column() { Name = "X2" });
var Default2 = new Table() { Name = "Table2" };
Default2.Columns.Add(new Column() { Name = "Y1" });
Default2.Columns.Add(new Column() { Name = "Y2" });
var DefaultSchema = new List<Table>() { Default1, Default2 };
var T1 = new Table() { Name = "Table1" };
T1.Columns.Add(new Column() { Name = "X1" });
var T2 = new Table() { Name = "Table2" };
T2.Columns.Add(new Column() { Name = "Y2" });
var MyTables = new List<Table>() { T1, T2};
/*
var DiffTables = DefaultSchema.Join(??).Select(x => x.Columns).Except(?? MyTables.Select(y => y.Columns) ...
*/
Expected result:
var DiffTables =
{
{
Name = "Table1",
Columns =
{
Name = "X2" //Missing from DefaultSchema.Table1
}
},
{
Name = "Table2",
Columns =
{
Name = "Y1" //Missing from DefaultSchema.Table2
}
}
}
Is there any way of doing this with a lamdba expression, or just by a master+nested foreach?
Thanks!
For comparing just two tables, it would be:
Default1.Columns
.Select(x => x.Name)
.Except(T1.Columns.Select(x => x.Name));
For comparing two schemas, it would be:
DefaultSchema
.Zip(MyTables, (x, y) => new
{ Name = x.Name,
MissingColumns = x.Columns.Select(x1 => x1.Name)
.Except(y.Columns.Select(y1 => y1.Name)) });
Zip combines any two sequences, so that item 1 gets matched with item 2, item 2 gets matched with item 2, etc. (in other words, like a zipper).
Except removes all items of one sequence from another sequence.
As #MetroSmurf pointed out, my original version had an error that was causing Except to fail. The reason is that the it was comparing the columns based on whether they are referring to the same object. I added the inner Select statements to allow the columns to be compared by Name instead.
Note also that this answer assumes the two schema being compared have the same tables in the same order.
Another way to go (inspired by #MetroSmurf's use of IEquatable) is to create a custom IEqualityComparer, like this:
public class ColumnComparer : IEqualityComparer<Column>
{
public bool Equals(Column x, Column y)
{
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) return false;
return x.Name == y.Name;
}
public int GetHashCode(Column column)
{
if (Object.ReferenceEquals(column, null)) return 0;
return column.Name.GetHashCode();
}
}
Then the LINQ query reduces down to just this:
DefaultSchema.Zip(MyTables, (x, y) => new
{
Name = x.Name,
MissingColumns = x.Columns.Except(y.Columns, new ColumnComparer())
});
Again, the same assumption that the tables are equivalent for the two schemas applies.
If this assumption doesn't apply (i.e., MyTables are not in order or may be missing tables), you can use a "left outer join" instead:
var result =
from table in DefaultSchema
join myTable in MyTables on table.Name equals myTable.Name into matchingTables
from matchingTable in matchingTables.DefaultIfEmpty()
select new
{
Name = table.Name,
MissingColumns = matchingTable == null
? null
: table.Columns.Except(matchingTable.Columns, new ColumnComparer())
};
With this query, a result is generated for every table in DefaultSchema. If one or more of the MyTables has the same name, the missing columns get reported. If the table is missing from MyTables, the value of MissingColumns is null. Note that this will not report on any extra tables in MyTables that don't exist in DefaultSchema.
Here is how you can do it:
var result =
DefaultSchema
.Select(
table =>
new
{
Table = table,
UserTable = MyTables.FirstOrDefault(utable => utable.Name == table.Name)
})
.Select(item => new
{
Name = item.Table.Name,
MissingColumns =
item.UserTable == null
? item.Table.Columns.Select(x => x.Name).ToArray()
: item.Table.Columns.Select(x => x.Name)
.Except(item.UserTable.Columns.Select(x => x.Name))
.ToArray()
}).ToList();
This code handles the case where the two lists are not guaranteed to have the same number of tables or to have the tables in the correct order.
It starts by selecting the default schema table with its corresponding user table (or null of the user table is not found).
Then, for each such object, it creates a new object that contains the default schema table name, and the list of missing columns.
The list of missing columns are all the columns in the default schema table if a corresponding user table is not found.
If a corresponding user table is found, Except is used to subtract the list of columns defined in the user table from the columns defined in the default schema table.
Using a complex Linq query is going to over-complicate what would be an otherwise easier to understand and maintainable loop. An alternative to devuxer's suggestion (which works great assuming everything in both lists can be zipped):
First, I'd implement IEquatable for an easy comparison:
public class Column : IEquatable<Column>
{
public string Name { get; set; }
public bool Equals( Column other )
{
// consider case insensitive comparison if needed.
return Name == other.Name;
}
}
The loop then becomes:
var diffs = new List<Table>();
foreach( Table table in MyTables )
{
Table schema = DefaultSchema
// consider case insensitive comparison if needed.
.FirstOrDefault( x => x.Name == table.Name );
if( schema == null )
{
// no matching schema, everything should be evaluated.
diffs.Add( table );
continue;
}
// use IEquatable to pull out the differences
List<Column> columns = table.Columns.Except( schema.Columns ).ToList();
if( columns.Any() )
{
diffs.Add( new Table { Name = table.Name, Columns = columns } );
}
}

return the only record value in a table

ClientAccountAccess table will only ever have one record, containing an ID and a GUID.
I want to return the GUID as string.
private static string GetAccessCode()
{
using (EPOSEntities db = new EPOSEntities())
{
string clientAccessCode = from e in db.ClientAccountAccesses
where string.IsNullOrWhiteSpace(e.GUID)
select e.GUID;
return clientAccessCode;
}
}
select is throwing an error saying cant convert Ienumerable to string, but I dont want to create an IEnumerable clientAccessCode, as I said there will only ever be one record in this table and I want to return the value of the GUID.
?
thanks for replies
Use FirstOrDefault() or SingleOrDefault() if there should not be more than one matched result:
private static string GetAccessCode()
{
using (EPOSEntities db = new EPOSEntities())
{
var clientAccessCodes = from e in db.ClientAccountAccesses
where string.IsNullOrWhiteSpace(e.GUID)
select e.GUID;
return clientAccessCodes.FirstOrDefault();
}
}
Note - result of query will be of type IEnumerable<string>. You also can use lambda syntax:
private static string GetAccessCode()
{
using (EPOSEntities db = new EPOSEntities())
{
return db.ClientAccountAccesses
.Where(e => String.IsNullOrWhiteSpace(e.GUID))
.Select(e => e.GUID)
.FirstOrDefault();
}
}

Combining Tables With Different Data Using Linq in MVC?

I have Two classes Named OfflineOrderLineItem.cs and OnlineOrderLineItem.cs both have diff Order table named offline and Online
In that i want to Combine the two tables data to search and Display the Fields from both tables
How to do that using linq in mvc4 ??? any idea.....
public virtual IPagedList<OnlineOrderLineItem> SearchOrderLineItems(string PoNumber)
{
var query1 = (from ol in _offlineOrderLineItemRepository.Table
select new
{
ol.Name
}).ToList();
var query2 = (from opv in _onlineOrderLineItemRepository.Table
select new
{
opv.Name
}).ToList();
var finalquery = query1.Union(query2);
if (!String.IsNullOrWhiteSpace(Name))
finalquery = finalquery.Where(c => c.Name == Name);
var orderlineitems = finalquery.ToList(); //its not working it throw a error
return new PagedList<OnlineOrderLineItem>(orderlineitems);//error
}
Error
cannot convert from 'System.Collections.Generic.List<AnonymousType#1>'
to 'System.Linq.IQueryable<Nop.Core.Domain.Management.OnlineOrderLineItem>'
to 'System.Linq.IQueryable<Nop.Core.Domain.Management.OnlineOrderLineItem>'
query1 and query2 are lists of an anonymous type with a single property of type string. (I assmume the ol.Name and opv.Name are strings.) Hence finalQuery and orderlineitems are collections of this anonymous as well. By specifying PagedList<T> you require that the collection passed into the constructor is an enumeration of type T. T is OnlineOrderLineItem, but the enumeration passed into the constructor is the anonymous type which is a different type. Result: compiler error.
To solve the problem I suggest that you define a named helper type that you can use to union the two different types OfflineOrderLineItem and OnlineOrderLineItem:
public class OrderLineItemViewModel
{
public int Id { get; set; }
public string PoNumber { get; set; }
public string Name { get; set; }
// maybe more common properties of `OfflineOrderLineItem`
// and `OnlineOrderLineItem`
}
Then your SearchOrderLineItems method should return a paged list of that helper type:
public virtual IPagedList<OrderLineItemViewModel> SearchOrderLineItems(
string PoNumber)
{
var query1 = from ol in _offlineOrderLineItemRepository.Table
select new OrderLineItemViewModel
{
Id = ol.Id,
PoNumber = ol.PoNumber,
Name = ol.Name,
// maybe more properties
};
// don't use ToList here, so that the later Union and filter
// can be executed in the database
var query2 = from opv in _onlineOrderLineItemRepository.Table
select new OrderLineItemViewModel
{
Id = opv.Id,
PoNumber = opv.PoNumber,
Name = opv.Name,
// maybe more properties
};
// don't use ToList here, so that the later Union and filter
// can be executed in the database
var finalquery = query1.Union(query2);
// again no ToList here
if (!string.IsNullOrWhiteSpace(PoNumber))
finalquery = finalquery.Where(c => c.PoNumber == PoNumber);
var orderlineitems = finalquery.ToList(); // DB query runs here
return new PagedList<OrderLineItemViewModel>(orderlineitems);
}
It is important to use ToList only at the very end of the query. Otherwise you would load the whole tables of all OnlineOrderLineItems and all OfflineOrderLineItems into memory and then filter out the items with the given PoNumber in memory which would be a big overhead and performance desaster.
Instead of
var orderlineitems = finalquery.ToList();
Try
var orderlineitems = finalquery.AsQueryable();
From https://github.com/TroyGoode/PagedList/blob/master/src/PagedList/PagedList.cs, PagedList takes a IQueryable<T>
Queryable.AsQueryable<TElement> Method

How to match the results back to an array

I have an array of objects. The object has two properties a value and an index.
I use a linq to entities query with the contains keyword to bring back all results in a table that match up to value.
Now here is the issue... I want to match up the results to the object index...
what is the fastest best way to perform this. I can add properties to the object.
It is almost like I want the query results to return this:
index = 1;
value = "searchkey"
queryvalue = "query value"
From your question I think I can assume that you have the following variables defined:
Lookup[] (You look-up array)
IEnumerable<Record> (The results returned by your query)
... and the types look roughly like this:
public class Lookup
{
public int Index { get; set; }
public int Value { get; set; }
}
public class Record
{
public int Value { get; set; }
/* plus other fields */
}
Then you can solve your problem in a couple of ways.
First using an anonymous type:
var matches
= from r in records
join l in lookups on r.Value equals l.Value
group r by l.Index into grs
select new
{
Index = grs.Key,
Records = grs.ToArray(),
};
The other two just use standard LINQ GroupBy & ToLookup:
IEnumerable<IGrouping<int, Record>> matches2
= from r in records
join l in lookups on r.Value equals l.Value
group r by l.Index;
ILookup<int, Record[]> matches3
= matches2.ToLookup(m => m.Key, m => m.ToArray());
Do these solve your problem?
Just a shot in the dark as to what you need, but the LINQ extension methods can handle the index as a second paramter to the lambda functions. IE:
someCollection.Select( (x,i) => new { SomeProperty = x.Property, Index = i } );

Categories