Correct implementation of IGrouping to be used inside longlistselector groupheader - linq

I have an ObservableCollection like the following-
private ObservableCollection<KeyedList<int, Anime>> _grp;
public ObservableCollection<KeyedList<int, Anime>> GroupedAnimeByGenre
{
get
{
return _grp;
}
set
{
_grp = value;
RaisePropertyChanged("GroupedAnimeByGenre");
}
}
I am using this to populate a LongListSelector with grouping. The KeyedList is implemented like this-
public class KeyedList<TKey, TItem> : List<TItem>
{
public TKey Key { protected set; get; }
public KeyedList(TKey key, IEnumerable<TItem> items)
: base(items)
{
Key = key;
}
public KeyedList(IGrouping<TKey, TItem> grouping)
: base(grouping)
{
Key = grouping.Key;
}
}
I have the following code to feed the ObservableCollection. Keep in mind AnimeList2 is a temporary Collection.
var groupFinale = AnimeList2.GroupBy(txt => txt.id).Where(grouping => grouping.Count() > 1).ToObservableCollection();
GroupedAnimeByGenre = groupFinale ;
But I am unable to convert/use groupFinale with GroupedAnimeByGenre. I am missing the extension method part as I am not well aware of the syntax. Please help

If you remove the ToObservableCollection() call and take just that part
var groupFinale = AnimeList2.GroupBy(txt => txt.id).Where(grouping => grouping.Count() > 1);
you'll see that the type of groupFinale is IEnumerable<IGrouping<int, Anime>>. Hence applying ToObservableCollection() will result in ObservableCollection<IGrouping<int, Anime>>. However, the type of the GroupedAnimeByGenre is ObservableCollection<KeyedList<int, Anime>>. So you need to convert IEnumerable<IGrouping<int, Anime>> to IEnumerable<KeyedList<int, Anime>> which in LINQ is performed by the Select method.
Shortly, you can use something like this
var groupFinale = AnimeList2
.GroupBy(txt => txt.id)
.Where(grouping => grouping.Count() > 1)
.Select(grouping => new KeyedList<int, Anime>(grouping))
.ToObservableCollection();
You can make such conversion easier by providing an extension method (similar to BCL provided ToArray() / ToList()) that will allow skipping the type arguments like this
public static class KeyedList
{
public static KeyedList<TKey, TItem> ToKeyedList<TKey, TItem>(this IGrouping<TKey, TItem> source)
{
return new KeyedList<TKey, TItem>(source);
}
}
Then you can use simply
var groupFinale = AnimeList2
.GroupBy(txt => txt.id)
.Where(grouping => grouping.Count() > 1)
.Select(grouping => grouping.ToKeyedList())
.ToObservableCollection();

Related

linq any clause on multiple values

I am trying to create a linq statement to filter otu results using an any clause. My issue is that I dont have a single value to compare against.
In the example below I have a PropertyTaxBill entity that is the parent. Each one has a collection of TaxPropertyAssessmentDetails attached to it.
In this query people can specify that they only want to deal with bills pertaining to a specific class strata so I check to see if any values exist in the classStrata variable. If so then the user selected specific ones. I was trying to do an any clause on the classStrata but instead of giving it a single value to match on I was trying to select all the values in the TaxPropertyAssessmentDetails collection attached to the PropertyTaxBill. Is this possible?
using (var dataContext = contextProvider.GetContext())
{
var query = dataContext.PropertyTaxBills.Where(x => x.Id > 1);
var classStrata = new int[0];
if (classStrata != null && classStrata.Any())
{
query = query.Where(x => classStrata.Any(y => y == x.TaxPropertyAssessmentDetails.SelectMany(z => z.PropertyTaxClassStrataId)));
}
}
You need to use contains so that ef is able to translate to a ef query.
Not sure that i understood your model correctly. Here's a example with a model. Please tell me if i understood incorrect so i can fix it.
public void TestMethod1()
{
IEnumerable<TaxBill> TaxBills = new List<TaxBill>();
var query = TaxBills.Where(x => x.Id > 1);
var classStrata = new int[0];
if (classStrata != null && classStrata.Any())
{
query = query.Where(x => x.AssessmentDetails.Any(ad => ad.TaxClassStrataId.Any(cs => classStrata.Contains(cs))));
}
}
And the entities
public class TaxBill
{
public int Id { get; set; }
public ICollection<AsessmentDetails> AssessmentDetails { get; set; }
}
public class AsessmentDetails
{
public ICollection<int> TaxClassStrataId { get; set; }
}

LINQ-To-Sharepoint Multiple content types for a single list

I'm using SPMetal in order to generate entity classes for my sharepoint site and I'm not exactly sure what the best practice is to use when there are multiple content types for a single list. For instance I have a task list that contains 2 content types and I'm defining them via the config file for SPMetal. Here is my definition...
<List Member="Tasks" Name="Tasks">
<ContentType Class="LegalReview" Name="LegalReviewContent"/>
<ContentType Class="Approval" Name="ApprovalContent"/>
</List>
This seems to work pretty well in that the generated objects do inherit from WorkflowTask but the generated type for the data context is a List of WorkflowTask. So when I do a query I get back a WorkflowTask object instead of a LegalReview or Approval object. How do I make it return an object of the correct type?
[Microsoft.SharePoint.Linq.ListAttribute(Name="Tasks")]
public Microsoft.SharePoint.Linq.EntityList<WorkflowTask> Tasks {
get {
return this.GetList<WorkflowTask>("Tasks");
}
}
UPDATE
Thanks for getting back to me. I'm not sure how I recreate the type based on the SPListItem and would appreciate any feedback.
ContractManagementDataContext context = new ContractManagementDataContext(_url);
WorkflowTask task = context.Tasks.FirstOrDefault(t => t.Id ==5);
Approval a = new Approval(task.item);
public partial class Approval{
public Approval(SPListItem item){
//Set all properties here for workflowtask and approval type?
//Wouldn't there be issues since it isn't attached to the datacontext?
}
public String SomeProperty{
get{ //get from list item};
set{ //set to list item};
}
Linq2SharePoint will always return an object of the first common base ContentType for all the ContentTypes in the list. This is not only because a base type of some description must be used to combine the different ContentTypes in code but also it will then only map the fields that should definitely exist on all ContentTypes in the list. It is however possible to get access to the underlying SPListItem returned by L2SP and thus from that determine the ContentType and down cast the item.
As part of a custom repository layer that is generated from T4 templates we have a partial addition to the Item class generated by SPMetal which implements ICustomMapping to get the data not usually available on the L2SP entities. A simplified version is below which just gets the ContentType and ModifiedDate to show the methodology; though the full class we use also maps Modified By, Created Date/By, Attachments, Version, Path etc, the principle is the same for all.
public partial class Item : ICustomMapping
{
private SPListItem _SPListItem;
public SPListItem SPListItem
{
get { return _SPListItem; }
set { _SPListItem = value; }
}
public string ContentTypeId { get; internal set; }
public DateTime Modified { get; internal set; }
public virtual void MapFrom(object listItem)
{
SPListItem item = (SPListItem)listItem;
this.SPListItem = item;
this.ContentTypeId = item.ContentTypeId.ToString();
this.Modified = (DateTime)item["Modified"];
}
public virtual void MapTo(object listItem)
{
SPListItem item = (SPListItem)listItem;
item["Modified"] = this.Modified == DateTime.MinValue ? this.Modified = DateTime.Now : this.Modified;
}
public virtual void Resolve(RefreshMode mode, object originalListItem, object databaseObject)
{
SPListItem originalItem = (SPListItem)originalListItem;
SPListItem databaseItem = (SPListItem)databaseObject;
DateTime originalModifiedValue = (DateTime)originalItem["Modified"];
DateTime dbModifiedValue = (DateTime)databaseItem["Modified"];
string originalContentTypeIdValue = originalItem.ContentTypeId.ToString();
string dbContentTypeIdValue = databaseItem.ContentTypeId.ToString();
switch(mode)
{
case RefreshMode.OverwriteCurrentValues:
this.Modified = dbModifiedValue;
this.ContentTypeId = dbContentTypeIdValue;
break;
case RefreshMode.KeepCurrentValues:
databaseItem["Modified"] = this.Modified;
break;
case RefreshMode.KeepChanges:
if (this.Modified != originalModifiedValue)
{
databaseItem["Modified"] = this.Modified;
}
else if (this.Modified == originalModifiedValue && this.Modified != dbModifiedValue)
{
this.Modified = dbModifiedValue;
}
if (this.ContentTypeId != originalContentTypeIdValue)
{
throw new InvalidOperationException("You cannot change the ContentTypeId directly");
}
else if (this.ContentTypeId == originalContentTypeIdValue && this.ContentTypeId != dbContentTypeIdValue)
{
this.ContentTypeId = dbContentTypeIdValue;
}
break;
}
}
}
Once you have the ContentType and the underlying SPListItem available on your L2SP entity it is simply a matter of writing a method which returns an instance of the derived ContentType entity from a combination of the values of the base type and the extra data for the missing fields from the SPListItem.
UPDATE: I don't actually have an example converter class as we don't use the above mapping extension to Item in this way. However I could imagine something like this would work:
public static class EntityConverter
{
public static Approval ToApproval(WorkflowTask wft)
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = wft.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = wft.SPListItem["field-name"];
return a;
}
}
Or you could put a method on a partial instance of WorkflowTask to return an Approval object.
public partial class WorkflowTask
{
public Approval ToApproval()
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = this.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = this.SPListItem["field-name"];
return a;
}
public LegalReview ToLegalReview()
{
// Create and return LegalReview as for Approval
}
}
In either situation you would need to determine the method to call to get the derived type from the ContentTypeId property of the WorkflowTask. This is the sort of code I would normally want to generate in one form or another as it will be pretty repetitive but that is a bit off-topic.

How to create AutoMap feature for FluentNHibernate based on attributes?

I want to use map mechanism, based on property attribute marking, like this:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class DomainSignatureAttribute : Attribute { }
public class SomeThing {
[DomainSignature]
public virtual string SomePropertyForMapping {get;set;}
[DomainSignature]
public virtual int OtherPropertyForMapping {get;set;}
public virtual string OtherPropertyWithoutMapping {get;}
}
So in ClassMap children I want to realise this method, that map all properties, marked by DomainSignatureAttribute attribute:
protected void MapPropertiesWithStandartType()
{
foreach (PropertyInfo property in typeof(T).GetProperties())
{
if (property != null)
{
object[] domainAttrs = property.GetCustomAttributes(typeof (DomainSignatureAttribute), true);
if (domainAttrs.Length > 0)
Map(x => property.GetValue(x, null));
}
}
}
But there is a small problem with Linq. When FluentNHibernate builds mapping (Cfg.BuildSessionFactory()) it break with
Tried to add property 'GetValue' when already added.
exception. As I undestand I need to rebuild Linq expression: x => property.GetValue(x, null) into correct form.
Please don't suggest to use AutoMap feature and don't suggest to use manual mapping.
var domainproperties = typeof(T).GetProperties()
.Where(prop => prop.GetCustomAttributes(typeof (DomainSignatureAttribute), true).Length > 0);
foreach (var property in domainproperties)
{
Map(Reveal.Member<T>(property.Name));
}

Passing an expression to a method in NHibernate results in Object of type 'ConstantExpression' cannot be converted to type 'LambdaExpression'

This problem occurs in both NHibernate 2 and 3. I have a Class A that has a member set of class B. Querying the classes directly executes nicely. But when I pass one of the expressions involving class B into a method I get the following error:
System.ArgumentException: Object of type 'System.Linq.Expressions.ConstantExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.
As far as I can see I am passing the exact same expression into the Any() method. But for some reason they are treated differently. I have done some debugging and it looks like in the first method, the expression is treated as an expression with NodeType 'Quote', while the same expression in the 2nd method seems to be treated as an expression with NodeType 'Constant'. The parent expression of the expression in the 2nd method has a NodeType 'MemberAccess'. So it looks like the expression tree is different in the different test methods. I just don't understand why and what to do to fix this.
Classes involvend:
public class A
{
public virtual int Id { get; set; }
public virtual ISet<B> DataFields { get; set; }
}
public class B
{
public virtual int Id { get; set; }
}
Sample test code:
[TestMethod]
public void TestMethod1()
{
using (ISession session = sessionFactory.OpenSession())
{
var records = session.Query<A>()
.Where<A>(a => a.DataFields
.Any(b => b.Id == 1));
Console.Write("Number of records is {0}", records.Count());
}
}
[TestMethod]
public void TestMethod2()
{
GetAsWhereB(b => b.Id == 1);
}
private void GetAsWhereB(Func<B, bool> where)
{
using (ISession session = sessionFactory.OpenSession())
{
var records = session.Query<A>()
.Where(a => a.DataFields
.Any(where));
Console.Write("Number of records is {0}", records.Count());
}
}
This is one problem:
private void GetAsWhereB(Func<B, bool> where)
That's taking a delegate - you want an expression tree otherwise NHibernate can't get involved. Try this:
private void GetAsWhereB(Expression<Func<B, bool>> where)
As an aside, your query is hard to read because of your use of whitespace. I would suggest that instead of:
var records = session.Query<A>().Where<A>(a => a.DataFields.
Any(b => b.Id == 1));
you make it clear that the "Any" call is on DataFields:
var records = session.Query<A>().Where<A>(a => a.DataFields
.Any(b => b.Id == 1));
I'd also suggest that you change the parameter name from "where" to something like "whereExpression" or "predicate". Some sort of noun, anyway :)
Not quite sure if this is the proper solution or not. The problem feels like a bug and my solution like a workaround. Nonetheless the following works for me, which boils down to creating a 'copy' of the given expression by using its body and parameter to construct a new expression.
private void GetAsWhereB(Func<B, bool> where)
{
Expression<Func<T, bool>> w = Expression.Lambda<Func<T, bool>>(where.Body, where.Parameters);
using (ISession session = sessionFactory.OpenSession())
{
var records = session.Query<A>()
.Where(a => a.DataFields
.Any(w));
Console.Write("Number of records is {0}", records.Count());
}
}

LINQ InvalidOperationException when making a query

Ok, last edit, promise:
With the following class:
public partial class MembershipModule : BaseConnection<MembershipModule>
{
private const string AccessPrivilege = "Access";
public bool Accessible()
{
return this.Privileges().Any(p => p.Name.Equals(AccessPrivilege));
}
public IQueryable<MembershipAction> Privileges()
{
var privileges = from p in LinqUtil.Context.MembershipModuleActions
where
p.MembershipModule.Id.Equals(this.Id) &&
p.MembershipRolePrivileges.Any(rp => rp.ModuleActionId.Equals(p.Id))
select p.MembershipAction;
return privileges;
}
}
Why does this work
public static List<MembershipModule> Collection()
{
List<MembershipModule> collection = new List<MembershipModule>();
if(!MembershipUser.Connected)
return collection;
foreach(MembershipModule m in LinqUtil.Context.MembershipModules)
{
if(m.Accessible())
collection.Add(m);
}
return collection;
}
While this doesn't?
public static List<MembershipModule> Collection()
{
if(!MembershipUser.Connected)
return new List<MembershipModule>();
return LinqUtil.Context.MembershipModules.Where(m => m.Accessible()).ToList();
}
looks to me like you are trying to execute a linq to object operation and it is trying to convert it to a sql statement.
Keep in mind: If this were to succede, I expect that the sql related to the folowing call would happen once per MembershipModule:
return this.Privileges().Any(p => p.Name.Equals(AccessPrivilege));
Have you tried skipping using the Accesible method, and hitting the Privileges collection directly? Something like this the code below. I have changed the return statement. This is the gist, may not be 100% correct:
public static List<MembershipModule> Collection()
{
if(!MembershipUser.Connected)
return new List<MembershipModule>();
var mod = MembershipModule.SearchById(new Guid("b012d35f-6af1-47de-9e54-e5df957c07e1"));
var y = from p in LinqUtil.Context.MembershipModuleActions
where
p.MembershipModule.Id.Equals(mod.Id) &&
p.MembershipRolePrivileges.Any(rp => rp.ModuleActionId.Equals(p.Id))
select p.MembershipAction;
var u = y.Any(p => p.Name.Equals(AccessPrivilege));
return LinqUtil.Context.MembershipModules.Where(m => m.Privileges().Any(p => p.Name.Equals(AccessPrivilege)).ToList();
}
Could it have to do with a static method (public static List<_membershipModule> Collection()) of a class trying access instance members (AccessPrivilege)?

Resources