Dynamically create predicate with AND/OR? - linq

I have a method that creates a predicate based on the data from several fields. The fields can contain comma separated values. I can create a predicate that will OR the values in the field but then I need to AND the fields together.
if (model.MICA != null)
{
List<string> ids = ParseCSVList(model.MICA);
foreach (var id in ids)
{
predicate = predicate.Or(m => m.idtype == "MICA" && m.idnumber.Contains(id));
}
}
** I need to AND these two records together**
if (model.FRID != null)
{
List<string> ids = ParseCSVList(model.FRID);
foreach (var id in ids)
{
predicate = predicate.Or(e => e.idtype == "FRID" && e.idnumber.Contains(id));
}
}
When building a predicate is there an easy way to do that?

What's wrong with using predicate.And?

Related

Many-To-Many Entity Framework Update

I have an object that has a many-to-many relationship with another object. I am trying to write an update statement that doesn't result in having to delete all records from the many-to-many table first.
My data is:
StoredProcedure - StoredProcedureId, Name
Parameter - ParameterId, Name
StoredProcedure_Parameter - StoredProcedureId, ParameterId, Order
I have a UI for updating a stored procedured object (adding/removing parameters or changing the order of the parameters).
When I save, I end up at:
var storedProcedure = context.Sprocs.FirstOrDefault(s => s.SprocID == sproc.StoredProcedureId);
if (storedProcedure == null)
{
//do something like throw an exception
} else
{
storedProcedure.Name = sproc.Name;
//resolve Parameters many to many here
//remove all Params that are not in sproc.Params
//Add any params that are in sproc.Params but not in storedProcedure.Params
//Update the Order number for any that are in both
}
I know I could simply call .Clear() on the table and then reinsert all of the values with their current state (ensuring that all parameters that were removed by the UI are gone, new ones are added, and updated Orders are changed). However, I feel like there must be a better way to do this. Do many-to-many updates with EF usually get resolved by deleting all of the elements and reinserting them?
Here there is my code that I use and it works. The difference is that instead o having your 3 tables( StoredProcedure, StoredProcedure_Parameter and Parameter ) I have the following 3 tables: Order, OrdersItem(this ensure the many-to-many relation) and Item. This is the procedure that I used for updating or add an order, or after I change an existing OrderItem or add a new one to the Order.
public void AddUpdateOrder(Order order)
{
using (var db = new vitalEntities())
{
if (order.OrderId == 0)
{
db.Entry(order).State = EntityState.Added;
}
else
{
foreach (var orderItem in order.OrdersItems)
{
if (orderItem.OrderItemsId == 0)
{
orderItem.Item = null;
if (order.OrderId != 0)
orderItem.OrderId = order.OrderId;
db.Entry(orderItem).State = EntityState.Added;
}
else
{
orderItem.Order = null;
orderItem.Item = null;
db.OrdersItems.Attach(orderItem);
db.Entry(orderItem).State = EntityState.Modified;
}
}
db.Orders.Attach(order);
db.Entry(order).State = EntityState.Modified;
}
SaveChanges(db);
}
}

LINQ to Entities does not recognize the method 'System.String get_Item(System.String)' method

I've looked at the various solutions here but none of them seem to work for me, probably because I'm too new to all this and am groping in the dark a bit. In the code below, the object "appointment" contains some basic LDAP information. From a list of such objects I want to be able to get a single record, based on employee id. I hope the code here is sufficient to illustrate. FTR, I've tried various formulations, including trying to use from and a select. All fail with the error given in the Title above.
IQueryable<appointment> query = null;
foreach(var record in results)
{
BoiseStateLdapDataObject record1 = record;
query = db.appointments.Where(x => x.student_id == record1.Values["employeeid"]);
}
if (query != null)
{
var selectedRecord = query.SingleOrDefault();
}
Try to move employee id getting out of query:
IQueryable<appointment> query = null;
foreach(var record in results)
{
var employeeId = record.Values["employeeid"];
query = db.appointments.Where(x => x.student_id == employeeId);
}
if (query != null)
{
var selectedRecord = query.SingleOrDefault();
}

Distinct by Attribute inside the item on my collection

I have an IEnumerable<MyObject> collection, with N MyObject elements.
MyObject is a class with a Title, a Description and an ID (as string).
I'd like to have my collection with distinct list of MyObject, due to the ID field.
So if 2 MyObject have got the same ID, one should be deleted (don't care which, I need unique ID).
How can I do it with LINQ?
Tried :
myList = myList.GroupBy(o => o.ID);
but seems I need a cast?
You can implement a custom IEqualityComparer<MyObject>. Then you can use Enumerable.Distinct to filter out duplicates.
class DistinctIdComparer : IEqualityComparer<MyObject> {
public bool Equals(MyObject x, MyObject y) {
return x.Id == y.Id;
}
public int GetHashCode(MyObject obj) {
return obj.Id.GetHashCode();
}
}
Now it's simple:
IEnumerable<MyObject> distinct = myObjects.Distinct(new DistinctIdComparer());
Or you can use Enumerable.GroupBy what is even simpler:
distinct = myObjects.GroupBy(o => o.ID)
.Select(g => g.First());
If you wants only unique id then you can try.
var uniqueIds = myObjects.Select(x=>x.ID).Distinct();
Or for Unique ID Objects
List<MyObject> objs = new List<MyObject> ();
myObjects.ForEach(x=>{
if(objs.Find(y=>y.ID == x.ID)== null)
objs.Add(x);
});

Simple.Data Many-to-many Issues

So I'm working through a simplified example of my hoped-for database that has the following tables:
Contractors: Id, ContractorName
Types: Id, TypeName
CoverageZips: ContractorId, Zip
TypesForContractors: ContractorId, TypeId
where contractors can have many zips and types and types and zips can have many contractors (many-to-many).
I'm trying to:
do a search for contractors in a certain zip code
then load the types for those contractors.
The SQL for the first part would probably look like:
SELECT * FROM dbo.Contractors WHERE Id IN
(SELECT ContractorId FROM dbo.CoverageZips WHERE Zip = 12345)
Here's what I have for the first part in Simple.Data. It's working, but I feel like I'm missing some of the beauty of Simple.Data...
List<int> contractorIds = new List<int>();
foreach(var coverage in _db.CoverageZips.FindAllByZip(zip)) {
contractorIds.Add((int)coverage.ContractorId);
}
var contractors = new List<dynamic>();
if (contractorIds.Count > 0) {
contractors = _db.Contractors.FindAllById(contractorIds).ToList<dynamic>();
}
return contractors;
That's working ok until I try part 2:
public dynamic GetAllForZip(int zip) {
List<int> contractorIds = new List<int>();
foreach(var coverage in _db.CoverageZips.FindAllByZip(zip)) {
contractorIds.Add((int)coverage.ContractorId);
}
var contractors = new List<dynamic>();
if (contractorIds.Count > 0) {
contractors = _db.Contractors.FindAllById(contractorIds).ToList<dynamic>();
}
foreach (var contractor in contractors) {
// Exception occurs here on second iteration
// even though the second contractor was originally in the contractors variable
contractor.types = GetTypesForContractor((int)contractor.Id);
}
return contractors;
}
public dynamic GetTypesForContractor(int id) {
var types = new List<dynamic>();
if (id > 0) {
List<int> typeIds = new List<int>();
foreach (var typeForContractor in _db.TypesForContractor.FindAllByContractorId(id)) {
typeIds.Add((int)typeForContractor.TypeId);
}
if (typeIds.Count > 0) {
types = _db.ContractorTypes.FindAllById(typeIds).ToList<dynamic>();
}
}
return types;
}
I set a breakpoint and everything works ok for the first iteration showing , but is failing on the second with the following exception:
Index was out of range. Must be non-negative and less than the size of the collection.
tl;dr
I'm not sure how to properly use many-to-many relationships with Simple.Data and something weird is happening when I try my method more than once
I don't know what's happening with that exception and will investigate today.
You are missing some beauty, though. Assuming you have referential integrity configured on your database (which of course you do ;)), your methods can be written thus:
public dynamic GetAllForZip(int zip) {
var contractors = _db.Contractors
.FindAll(_db.Contractors.ContractorZips.Zip == zip)
.ToList();
foreach (var contractor in contractors) {
contractor.Types = GetTypesForContractor((int)contractor.Id);
}
return contractors;
}
public dynamic GetTypesForContractor(int id) {
return _db.ContractorTypes
.FindAll(_db.ContractorTypes.TypesForContractor.ContractorId == id)
.ToList();
}
Update!
As of 1.0.0-beta3, eager-loading across many-to-many joins is supported, so now you can do this:
public dynamic GetAllForZip(int zip) {
return _db.Contractors
.FindAll(_db.Contractors.ContractorZips.Zip == zip)
.With(_db.Contractors.TypesForContractor.ContractorTypes.As("Types"))
.ToList();
}
And that executes as a single SQL select to make your DBA happy like rainbow kittens.

Complex foreach loop possible to shorten to linq?

I have a cluttery piece of code that I would like to shorten using Linq. It's about the part in the foreach() loop that performs an additional grouping on the result set and builds a nested Dictionary.
Is this possible using a shorter Linq syntax?
var q = from entity in this.Context.Entities
join text in this.Context.Texts on new { ObjectType = 1, ObjectId = entity.EntityId} equals new { ObjectType = text.ObjectType, ObjectId = text.ObjectId}
into texts
select new {entity, texts};
foreach (var result in q)
{
//Can this grouping be performed in the LINQ query above?
var grouped = from tx in result.texts
group tx by tx.Language
into langGroup
select new
{
langGroup.Key,
langGroup
};
//End grouping
var byLanguage = grouped.ToDictionary(x => x.Key, x => x.langGroup.ToDictionary(y => y.PropertyName, y => y.Text));
result.f.Apply(x => x.Texts = byLanguage);
}
return q.Select(x => x.entity);
Sideinfo:
What basically happens is that "texts" for every language and for every property for a certain objecttype (in this case hardcoded 1) are selected and grouped by language. A dictionary of dictionaries is created for every language and then for every property.
Entities have a property called Texts (the dictionary of dictionaries). Apply is a custom extension method which looks like this:
public static T Apply<T>(this T subject, Action<T> action)
{
action(subject);
return subject;
}
isn't this far simpler?
foreach(var entity in Context.Entities)
{
// Create the result dictionary.
entity.Texts = new Dictionary<Language,Dictionary<PropertyName,Text>>();
// loop through each text we want to classify
foreach(var text in Context.Texts.Where(t => t.ObjectType == 1
&& t.ObjectId == entity.ObjectId))
{
var language = text.Language;
var property = text.PropertyName;
// Create the sub-level dictionary, if required
if (!entity.Texts.ContainsKey(language))
entity.Texts[language] = new Dictionary<PropertyName,Text>();
entity.Texts[language][property] = text;
}
}
Sometimes good old foreach loops do the job much better.
Language, PropertyName and Text have no type in your code, so I named my types after the names...

Resources