Remove items from a collection in entity framework - sql

I have a function as below :
IEnumerable<Group> GetAllChildren(int parentID)
{
using (Entities db = new Entities())
{
var result = (from x in db.Groups
where x.ParentID == parentID
select x).ToList();
foreach (Group child in result.ToList())
{
result.AddRange(GetAllChildren(child.GroupID));
}
return result;
}
}
In the above function if I pass a group name I get all the children at all levels.
It works as expected.
Now my query looks like something like :
GroupNamesWithCorrespondingEffects
= new ObservableCollection<GroupNameWithCorrespondingEffect>
(from g in db.Groups
select new GroupNameWithCorrespondingEffect
{
GroupID = g.GroupID,
GroupName = g.GroupName,
CorrespondingEffect = g.Master_Effects.Effect
}
);
The above query will give me all the groups.
Now I want to remove all the groups from GroupNamesWithCorrespondingEffects that are children of a group with id == 25.
I have tried .Remove(GetAllChildren(25)) in 2nd query. but I get following error.
Collection.Remove(GroupNameWithCorrespondingEffect) has some invalid arguments.

hope this help you:
var childs = GetAllChildren(25).ToList();
var childIDList = childs.select(u => u.GroupID).ToList();
GroupNamesWithCorrespondingEffects = GroupNamesWithCorrespondingEffects
.Where(u => !childIDList.Contains(u.GroupID)).ToList();

Related

Raven DB filter on subset of array items and sort on the cheapest of the filter results items

Assuming i have an parent class that I filter on various properties, one of which is a property that is an array of items .
Now say that i want to only return the parent item if my array of items as above a min value and below a max value ...that's fine i can work that bit out;
What if i then want to then sort on the filtered result set of those items
I made a c# fiddle example to show what im trying to achieve :
https://dotnetfiddle.net/mV4d28
(note that foo2 is returned first even though foo1 has items in its array that are less that those in foo2)
As i need to do this using a index i need the index to be able to compute the order by based on the filter criteria used in my query.
I know elasticsearch has an inner hits function that dose this and mongo has pipelines which also dose this so im sure Raven must have a way of doing this too ?
I was hoping using just index and a transform with prams i could achieve this so I tried it:
my index and transform look like this
public class familyTransfrom : AbstractTransformerCreationTask<ParentItem>
{
public class Result : ParentItem{
public double[] ChildItemValuesFiltered { get; set; }
}
public familyTransfrom(){
TransformResults = parents => from parent in parents
let filterMinValue = Convert.ToDouble(ParameterOrDefault("FilterMinValue", Convert.ToDouble(0)).Value<double>())
let filterMaxValue = Convert.ToDouble(ParameterOrDefault("FilterMaxValue", Convert.ToDouble(9999)).Value<double>())
select new Result{
ParentItemId = parent.ParentItemId,
ParentItemName = parent.ParentItemName,
ParentItemValue = parent.ParentItemValue,
//ChildItemValuesFiltered = parent.ChildItems.Where(p => p.ChildItemValues.Any(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).SelectMany(t => t.ChildItemValues).ToArray<double>(),
ChildItemValuesFiltered = parent.ChildItems.SelectMany(p => p.ChildItemValues.Where(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).ToArray<double>(),
ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()
};
}
}
public class familyIndex : AbstractIndexCreationTask<ParentItem>{
public class Result : ParentItem {
public double[] ChildItemValues { get; set; }
}
public familyIndex(){
Map = parents => from parent in parents
select new Result{
ParentItemId = parent.ParentItemId,
ParentItemName = parent.ParentItemName,
ParentItemValue = parent.ParentItemValue,
ChildItemValues = parent.ChildItems.SelectMany(p => p.ChildItemValues.Select(y => y)).ToArray(),
ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()
};
Index("ParentItemId", FieldIndexing.Analyzed);
Index("ParentItemName", FieldIndexing.Analyzed);
Index("ParentItemValue", FieldIndexing.Analyzed);
Index("ChildItemValues", FieldIndexing.Analyzed);
Index("ChildItems", FieldIndexing.Analyzed);
}
}
my query is as follows , (this is using the live raven playground so this should just work out of the box it you want to use it)
using (IDocumentStore store = new DocumentStore { Url = "http://live-test.ravendb.net/", DefaultDatabase = "altha" })
{
store.Initialize();
using (IDocumentSession session = store.OpenSession())
{
if(1 == 2){
//foreach (ParentItem element in data.OfType<ParentItem>()) {
// session.Store((ParentItem)element);
// session.SaveChanges();
//}
new familyIndex().Execute(store);
new familyTransfrom().Execute(store);
}else{
double filterMinValue = 3.0;
double filterMaxValue = 4.0;
var results = session
.Advanced
.DocumentQuery<familyIndex.Result,familyIndex>()
.WhereBetweenOrEqual("ChildItemValues", filterMinValue, filterMaxValue)
.SetResultTransformer<familyTransfrom, familyTransfrom.Result>()
.SetTransformerParameters(new Dictionary<string, RavenJToken> {
{ "FilterMinValue", filterMinValue },
{ "FilterMaxValue", filterMaxValue } })
.OrderBy("ChildItemValues")
.OfType<ParentItem>().ToList();
results.Dump();
}}
}
What i found was i cant use "ChildItemValuesFiltered" from the transform result as its not index. So unless i can order by the result of a transform ? i couldn't get this to work as although it filters it dosnt order correctly.
Is there another to achieve what i want using projections or intersection or rank or reduce try method ?
I was thinking if i had to perhaps i could use https://ravendb.net/docs/article-page/3.5/csharp/indexes/querying/sorting#custom-sorting
and do something like this:
public class SortByNumberOfCharactersFromEnd : IndexEntriesToComparablesGenerator
{
private readonly double filterMinValue;
private readonly double filterMinValue;
public SortByNumberOfCharactersFromEnd(IndexQuery indexQuery)
: base(indexQuery)
{
filterMinValue = IndexQuery.TransformerParameters["FilterMinValue"].Value<double>(); // using transformer parameters to pass the length explicitly
filterMaxValue = IndexQuery.TransformerParameters["FilterMaxValue"].Value<double>();
}
public override IComparable Generate(IndexReader reader, int doc)
{
var document = reader.Document(doc);
double[] childItemValues = (double[])document.GetValues("ChildItemValuesFiltered").Select(double.Parse).ToArray(); // this field is stored in index
return childItemValues.Where(x => x >= min && x <= max).Min();
}
}
then do a where filter and order by clause using index and transform passing in the same prams that i use in the where filter . however im not sure if this would work ?
More importantly im not sure how i go about getting the sort dll into the plugins ie what name space should the class go under, what name spaces dose it need to import, what assembly name dose it need to use etc
According to https://ravendb.net/docs/article-page/3.5/csharp/server/plugins/what-are-plugins i just need to drop the dll in and raven will this this up , however i cant seem to find what name space i need to reference for IndexEntriesToComparablesGenerator ?
im using linqpad 5 to test my stuff ...so in order to use the custom order i have to reference the class
any tips or advice or how to guild/examples welcome
so it didn't occur to me that i could do the filtering in the transform
TransformResults = parents => from parent in parents
let filterMinValue = Convert.ToDouble(ParameterOrDefault("FilterMinValue", Convert.ToDouble(0)).Value<double>())
let filterMaxValue = Convert.ToDouble(ParameterOrDefault("FilterMaxValue", Convert.ToDouble(9999)).Value<double>())
select new {
ParentItemId = parent.ParentItemId,
ParentItemName = parent.ParentItemName,
ParentItemValue = parent.ParentItemValue,
//ChildItemValuesFiltered = parent.ChildItems.Where(p => p.ChildItemValues.Any(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).SelectMany(t => t.ChildItemValues).ToArray<double>(),
ChildItemValuesFiltered = parent.ChildItems.SelectMany(p => p.ChildItemValues.Where(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).ToArray<double>(),
ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()
} into r
where r.ChildItemValuesFiltered.Length > 0
orderby r.ChildItemValuesFiltered.Min()
select r;
This gives me what i wanted, here are the sample query:
http://live-test.ravendb.net/databases/altha/indexes/familyIndex?start=0&pageSize=25&resultsTransformer=familyTransfrom&tp-FilterMinValue=3&tp-FilterMaxValue=4
i cant take credit for this as guys at raven helped me but sharing the knowledge for others

Linq Multiple Expression

I use ASP.NET and EF5. I need multiple condition with Linq. I need like this sql statement.
SELECT * FROM Products
WHERE (ProdName LIKE '%value1%' AND ProdName LIKE '%value2%' AND ProdName LIKE '%value3%')
OR (Keywords LIKE '%value4%')
How can i convert to linq the above sql. I've write this but it's not enough.
var prodQuery = from p in _db.Products
select new SearchResult
{
Product = p,
SearchResultType = SearchResultType.Prod
};
//this foreach has to be in brackets in sql
foreach (var s in searchText.Split(' '))
{
var temp = s;
prodQuery = prodQuery.Where(x => x.Product.ProdName.Contains(temp));
}
//and this foreach has to be in another bracket in sql and it has to connect with or clause
prodQuery = prodQuery.Where(x => x.Product.Keywords.Contains(searchText));
Try this:
var keywords = searchText.Split(' ');
var results = _db.Products.Where(p => keywords.Any(kw => kw.Contains(p.ProdName))
|| p.Keywords.Contains(searchText))
.Select(x => new SearchResult()
{
Product = x,
SearchResultType = SearchResultType.Prod
});

get list of decision for a specific meetingtitle using linq asp.net

I have a database table. What I want is to get data using group by clause as I have used in below code.
Note that Decision is another table. now I want that all the decisions related to a specific Meeting Title should be shown in list.like
meetingtitle1=decision1,decison2,decison3
meetingtitle2=decision1,decison2
but below code returns only one decisiontitle.
public List<NewMeetings> GetAllMeetings()
{
var xyz = (from m in DB.MeetingAgenda
//join mp in Meeting on m.MeetingId equals mp.MeetingId
//where m.MeetingId == 2
group m by new { m.Meeting.MeetingTitle } into grp
select new NewMeetings
{
// meetingid = grp.Key.MeetingId,
meetingtitle = grp.Key.MeetingTitle,
decision = grp.Select(x => x.Decision.DecisionTitle).FirstOrDefault(),
total = grp.Count()
}).ToList();
List<NewMeetings> list = xyz.ToList();
return list;
}
public class NewMeetings
{
public int meetingid;
public string meetingtitle;
public string decision;
public int total;
}
Can somebody please tell me how to return a list of decisions to a specific Meetingtitle?
You are doing a FirstOrDefault on the list of decisions which obviously means you are only getting a single value. Instead you can join them all together into one longer string (separated by commas as you indicated in the question) by changing this line:
decision = grp.Select(x => x.Decision.DecisionTitle).FirstOrDefault(),
To this:
decision = string.Join(",", grp.Select(x => x.Decision.DecisionTitle)),
However, as the string.Join is not recognised by Linq to Entities, you need to do the string.Join after the data has been retrieved (i.e. after the ToList):
var xyz = (from m in DB.MeetingAgenda
group m by new { m.Meeting.MeetingTitle } into grp
select new
{
meetingtitle = grp.Key.MeetingTitle,
decisions = grp.Select(x => x.Decision.DecisionTitle),
total = grp.Count()
})
.ToList()
.Select(m => new NewMeetings
{
meetingtitle = m.meetingtitle,
decision = string.Join(",", m.decisions),
total = m.total
});

Nhibernate Queryover with subquery get Next free number

How can I do this in nHibernate using queryover :
SELECT MIN(t.subid)+1 AS NextID
FROM subject t
WHERE NOT EXISTS
(SELECT id FROM subject n WHERE n.subid=t.subid+1)
Currently I have this but its not working because of this statement "SubId+1"
_session.QueryOver(() => subject)
.WithSubquery
.WhereNotExists(
subject
.Where(x => x.SubId==SubId+1)
.Select(x => x.Id)
)
.Select(Projections.ProjectionList()
.Add(Projections.Min<subject>(x => x.SubId)))
.List().First()
One way, using NOT IN instead of NOT EXISTS (results are the same) would be like this (the SQL query would be a bit different, but result will be the same)
Subjectsubject = null;
Subjectinner = null;
var subquery = QueryOver.Of<Subject>(() => inner)
.Select(Projections.SqlProjection(
// important the subid is the column name, not property name
" subid - 1 as innerId" // the trick here is, that we compare
, new[] { "innerId" } // inner ID - 1, with outer ID
, new IType[] { NHibernateUtil.Int32 }))
;
var id = session.QueryOver(() => subject)
.WithSubquery
.WhereProperty(() => subject.ID)
.NotIn(subquery)
.Select(Projections.ProjectionList().Add(Projections.Min<Subject>(s => s.ID)))
.SingleOrDefault<int>();
var id = id + 1 ; // here we increment in C#, instead of projecting that into SQL
Summary: not sure about algorithm you've used, but the trick how to get "subid - 1" is to use projection:
Projections.SqlProjection(
" subid - 1 as innerId" // sql statement
, new[] { "innerId" } // its alias
, new IType[] { NHibernateUtil.Int32 }) // type is int
NOTE: I would expect the last Projections to be MAX

dynamically change LINQ to Entity query

int year = 2009; // get summ of TONS2009 column
var query = from ODInfo in DataContext.CIMS_TRUCKS
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select new
{
OID = g.Key,
TotalTons = g.Sum( ODInfo => ODInfo.TONS2009)
};
IN the expression 'ODInfo => ODInfo.TONS2009', how do I change TONS2009 to TONS2010 or TONS2011 based on the method parameter 'int year' ?
K06a's answer is close but won't work server-side. Try this:
IEnumerable<OutputType> myQuery(IEnumerable<InputType> data, Expression<Func<InputType,decimal>> expr)
{
return from ODInfo in DataContext.CIMS_TRUCKS
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select new OutputType
{
OID = g.Key,
TotalTons = g.AsQueryable().Sum(expr)
};
}
var query = myQuery(DataContext.CIMS_TRUCKS, ODInfo => ODInfo.TONS2009);
I haven't tried this, but did something similar here.
UPDATE
If you really need to translate input strings (like "2009") to expressions, it's still possible:
string year = "2009";
Type ODInfoType = typeof(ODINFOTYPE); // substitute with the type of ODInfo
ParameterExpression pe = ParameterExpression.Parameter(ODInfoType, "ODInfo");
MemberInfo mi = ODInfoType.GetProperty("TONS" + year);
MemberExpression me = Expression.MakeMemberAccess(pe, mi);
var expr = Expression.Lambda<Func<ODINFOTYPE, decimal>>(me, pe);
Be aware that this is a patch to the extremly evil structure of your database.
You can try something like that:
TotalTons = g.Sum( ODInfo => (year == 2009) ? ODInfo.TONS2009 : ((year == 2010)
? ODInfo.TONS2010 : ODInfo.TONS2011))
Or make it more readable and use { } to split that lambda expression into more then one line and use eg. switch statement.
The best solution is to break this up into multiple querys that you can compose to a final query:
int year = 2009; // get summ of TONS2009 column
var odInfos =
year == 2009 ? DataContext.CIMS_TRUCKS.Select(x => new { x.OID, TONS = x.TONS2009 })
year == 2010 ? DataContext.CIMS_TRUCKS.Select(x => new { x.OID, TONS = x.TONS2010 })
year == 2011 ? DataContext.CIMS_TRUCKS.Select(x => new { x.OID, TONS = x.TONS2011 })
: null;
var query = from ODInfo in odInfos
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select new
{
OID = g.Key,
TotalTons = g.Sum(ODInfo => ODInfo.TONS)
};
This will specialize to three possible queries at runtime, thereby giving the best possible performance. It is better than a case-switch.
Try this way:
IEnumerable<OutputType> myQuery(IEnumerable<InputType> data, Func<InputType,decimal> func)
{
return from ODInfo in data
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select new OutputType
{
OID = g.Key,
TotalTons = g.Sum(func)
};
}
var query = myQuery(DataContext.CIMS_TRUCKS, ODInfo => ODInfo.TONS2009);
Using DynamicLinq which works with EF also:
int year = 2009; // get summ of TONS2009 column
var query = from ODInfo in DataContext.CIMS_TRUCKS
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select g;
var projectedGroups = query.Select("new (Key as OID, Sum(TONS" + year + ") as TotalTons)");