Linq-to-sql logic pain - c#

I've been trying to get the following method cleaned up using more sensible and lean syntax, but I'm striking serious headaches when it comes to aggregate clauses and filtering using L2S. Particularly, I feel I should be able to use a .Contains() method to filter out objects whose tags fit the string parameter passed in the method, but it hasn't worked.
public TagListViewModel GetTagModel(string Name)
{
var model = new TagListViewModel();
var repo = new SimpleRepository("Wishlist");
var ideas = repo.All<Idea>();
List<Idea> ideaList = new List<Idea>();
foreach (Idea i in ideas)
{
var query = from tag in repo.All<Tag>()
join ideatag in repo.All<IdeaTag>()
on tag.ID equals ideatag.TagId
where ideatag.IdeaId == i.ID
select tag;
i.Tags = query.ToList<Tag>();
ideaList.Add(i);
}
foreach (Idea i in ideaList)
{
var query = from vote in repo.All<IdeaVotes>()
where vote.IdeaId == i.ID
select vote;
i.Votes = query.ToList<IdeaVotes>();
}
// Here begins the problem area. I should be able to get a tag from the repo
// whose name matches the "Name" parameter and then call a .Contains() method to
// filter this list, shouldn't I?
List<Idea> filteredTagList = new List<Idea>();
foreach (Idea item in ideaList){
foreach(Tag t in item.Tags)
{
if (t.Name == Name)
filteredTagList.Add(item);
}
}
model.Ideas = filteredTagList;
return model;
}
It's ugly. I know it's ugly but after over 2 hours of playing with several preferred variations I still can't get it to filter the way it's supposed to. Where am I going wrong?

This should be equivalent assuming there are no duplicate tags on a single Idea.
model.Ideas = ideaList.Where(
idea => idea.Tags.Any(
tag => tag.Name == Name)).ToList();

Related

EF - finding items which are not in the list of integers - resulting query doesn't use parameters

I am using the following method:
public PagedResult<PaymentPlanItems> GetPagedRequest(SearchRequest searchRequest, List<int> idsToExclude)
{
var list = _dbSetList.AsQueryable();
if (idsToExclude != null)
{
list = list.Where(item => !idsToExclude.Contains(item.ItemId));
}
var query = SearchHelper.GetFilteredSearch<PaymentPlanItems>(list, searchRequest);
var pagedResultMessage = SearchHelper.GetPagedResult(query, searchRequest);
return pagedResultMessage;
}
where idsToExclude I originally got using this (found from another SO thread):
if (!string.IsNullOrEmpty(searchViewModel.ItemsToExclude))
{
List<int> idsToExclude = new List<int>(Array.ConvertAll(searchViewModel.ItemsToExclude.Split(','), int.Parse));
searchViewModel.Result = _paymentPlanItemsAdapter.GetPagedRequest(searchViewModel.SearchRequest, idsToExclude);
}
I then look at the generated query using profile and I see that I get the following as part of my query:
WHERE ( NOT ([Extent1].[ItemId] IN (440, 1017)))
I don't know if I should be really concerned as my numbers get inserted into the query directly and not as parameters and if I should be, what modifications to this method / approach I should take to make this query use parameters?

C# LINQ Join Object Model Collections and then filter by a property

I've been trying to write a LINQ statement that will compare a property from two collections of the same type of object model and produce a third collection of just those where the OnOff property is different. I've only been using LINQ for about a week now so I'm still getting used to it.
My understanding is a join will combine two collections and discard any items from them that are exactly the same, so you don't have duplicates.
I got advice to try a Join to do this and I'm getting close but I'm not there yet.
My model has string properties for Name, OnOff, Color, etc... I am trying to match models in the collections by name, and then give me a the models where the OnOff property is different.
Written out what I need is, for each LayerModel in SourceDrawingLayers, look into every LayerModel in TargetDrawingLayers and see if you find one with a matching Name. If you do, check the OnOff property. If it is NOT equal, Add it to the OnOffConflictLayers collection.
I've tried doing this with nested foreach statements and from x from y LINQ queries but I get a ton of duplicates. That's when Join was suggested to me. I just need help taking it home.
so far I have this:
var onOffQuery = from source in SourceDrawingLayers
join target in TargetDrawingLayers
on source.Name equals target.Name
//give me every target where target.OnOff does not equal source.OnOff
select new { target.OnOff }; //I don't know if I need this line it's just where I'm stuck at right now.
when finished I have to populate a collection with the result:
ObservableCollection<LayerModel> q = new ObservableCollection<LayerModel>(onOffQuery);
OnOffConflictLayers = q;
I would greatly appreciate help with this (probably super simple) query I am trying to run. Thanks!
Update: See my edit below.
private void GetOnOffConflicts()
{
DataAccess da = new DataAccess();
if (TargetDrawingLayers != null && TargetDrawingLayers.Count != 0)
{
TargetDrawingLayers.Clear();
}
if (OnOffConflictLayers != null && OnOffConflictLayers.Count != 0)
{
AllConflictLayers.Clear();
}
foreach (TargetDrawingModel targetDrawingModel in TargetDrawings)
{
foreach (var result in da.GetDrawingLayers(targetDrawingModel.DrawingPath))
{
TargetDrawingLayers.Add(result);
//everything works fine up to here. My collection is populated correctly.
}
}
var onOffQuery = from source in SourceDrawingLayers
from target in TargetDrawingLayers
where source.Name == target.Name && source.OnOff != target.OnOff
select target;
ObservableCollection<LayerModel> q = new ObservableCollection<LayerModel>(onOffQuery);
OnOffConflictLayers = q;
}
Edit: Matt U's answer below is correct. I mistakenly had not set up my target drawing correctly and all of the on/off properties actually were the same, so no wonder my code returned nothing.
Marked Matt's answer as correct.
It sounds like you're looking for what's known as a non-equijoin. Instead of a join, you'll use two from clauses and a where.
var onOffQuery = from source in SourceDrawingLayers
from target in TargetDrawingLayers
where source.Name == target.Name && source.OnOff != target.OnOff
select target;
// onOffQuery should contain the "target" objects where there is a matching Name in source, but OnOff differs.
// You can "select source" if you want the source objects
More on non-equijoin and other custom "joins": https://learn.microsoft.com/en-us/dotnet/csharp/linq/perform-custom-join-operations

Formatting Select Statement Using Dynamic Linq

I've been looking into this for quite some time now and cannot figure out a resolution. I Originally Tried formatting A dynamic linq Statement as you can see here in this post
I declared a class:
public class DynamicHelper
{
public string FormattedLink(string DisplayText, string ID)
{
return "" + DisplayText + "";
}
public string FormattedLink(string DisplayText, int ID)
{
return "" + DisplayText + "";
}
}
After I inserted a new type in DynamicLinq into the predefinedTypes
,typeof(DynamicHelper) //around line 635
I have a program which is attempting to Invoke the FormattedLink inside of a dynamic linq select:
using (var Model = new MK3Entities())
{
DynamicHelper Dh = new DynamicHelper();
var TOrigin = (Model.Titles.Where("ID > 19632")
.Select("new(ID, #0.FormattedLink(ExtTitleID, ID) as ExtTitleID )", Dh) as System.Collections.IEnumerable)
.Cast<dynamic>().Take(10).ToList();
Console.ReadKey();
}
When I execute this program I get a runtime exception "LINQ to Entities does not recognize the method 'System.String FormattedLink(System.String, Int32)' method, and this method cannot be translated into a store expression."
Any Ideas on how to fix this... I just need simple formatting from Dynamic Select.
The error message is pretty self explanatory. The database doesn't know how to translate that method into SQL. You need to fetch the information that the method needs in your database query and then call that function on the results, rather than in the query.
I'm not sure why you need it to be dynamic, it seems the solution you present is very overly complicated. I would write it as:
using (var Model = new MK3Entities())
{
DynamicHelper Dh = new DynamicHelper();
var TOrigin = Model.Titles
.Where("ID > 19632")
.Select(t => new { ID = t.ID, ExtTitleID = t.ExtTitleId })
.Take(10)
.ToList() // Execute SQL Statement
.Select(t => new {ID = t.ID, Link = nh.FormattedLink(ExtTitleID, ID)})
.ToList();
Console.ReadKey();
}
I'm returning an List<anonymous'1> object instead of a dynamic object (because I've never had the need for dynamic objects) so you can adjust it accordingly.
I just solved similiar problem few hours back.
YOu need ToList() that works with Dynamic linq. Check out this thread: Can't find property or field on a dynamic object
Just copy paste those to your project, and later:
var TOrigin = (Model.Titles.Where("ID > 19632")
.ToAnonymousList()
.Select("new(ID, #0.FormattedLink(ExtTitleID, ID) as
ExtTitleID )", Dh) as System.Collections.IEnumerable);

What's wrong with this LINQ, converting one object to another?

I'm trying to have a one-liner LINQ statement to project my one object to my ViewModel object - it seems it won't work with select?? The compiler says cannot resolve symbol select. This works with a collection, why doesn't it here? If not select, what keyword am I missing?
return from p in SettingRepository.Get(id)
select new EditSetting
{
};
Edit - Scratch that, doesn't work for a list. How can I accomplish this?
Although I'm not sure why you would want to use select to do this, but if you're really adamant about it:
return from p in new List<EntityObject>{SettingRepository.Get(id)}
select new EditSetting
{
};
If not, why not just use good old initializers?
var editSetting= new EditSetting { Id = setting.Id };
Typically I prefer to create methods with the following pattern. It discourages tight looping of Database calls, which can severely limit performance. Still Bryan Hong's answer is the actual ANSWER to your question.
public IQueryable<EditSetting> GetEditSettings()
{
return from p in SettingRepository
select new EditSetting{
Foo = p.Foo,
Bar = p.Bar,
//etc...
};
}
or
public IEnumerable<EditSetting> GetEditSettingsById(IList<string> ids)
{
var ret = from p in SettingRepository
where ids.Contains(p.Id)
select new EditSetting{
Foo = p.Foo,
Bar = p.Bar,
//etc...
};
return ret.ToList();
}

Creating a completely dynamic query with RavenDB using LuceneQuery

I want one method that can query my entire RavenDB database.
My method signature looks like this:
public static DataTable GetData(string className, int amount, string orderByProperty, string filterByProperty, string filterByOperator, string filterCompare)
I figured I can accomplish all of the above with a dynamic LuceneQuery.
session.Advanced.LuceneQuery<dynamic>();
The problem is: Since I'm using dynamic in the type given, how do I ensure that the query only includes the types matching the className?
I'm looking for something like .WhereType(className) or .Where("type: " + className).
Solution
This returns the results of the correct type:
var type = Type.GetType("Business.Data.DTO." + className);
var tagName = RavenDb.GetTypeTagName(type);
using (var session = RavenDb.OpenSession())
{
var result = session.Advanced
.LuceneQuery<object, RavenDocumentsByEntityName>()
.WhereEquals("Tag", tagName)
.ToList();
}
Note, it is not possible to add additional "WhereEquals" or other filters to this. This is because nothing specific to that document type is included in the "RavenDocumentByEntityName" index.
This means that this solution cannot be used for what I wanted to accomplish.
What I ended up doing
Although it doesn't fulfill my requirement completely, this is what I ended up doing:
public static List<T> GetData<T>(DataQuery query)
{
using (var session = RavenDb.OpenSession())
{
var result = session.Advanced.LuceneQuery<T>();
if (!string.IsNullOrEmpty(query.FilterByProperty))
{
if (query.FilterByOperator == "=")
{
result = result.WhereEquals(query.FilterByProperty, query.FilterCompare);
}
else if (query.FilterByOperator == "StartsWith")
{
result = result.WhereStartsWith(query.FilterByProperty, query.FilterCompare);
}
else if (query.FilterByOperator == "EndsWith")
{
result = result.WhereEndsWith(query.FilterByProperty, query.FilterCompare);
}
}
if (!string.IsNullOrEmpty(query.OrderByProperty))
{
if (query.Descending)
{
result = result.OrderBy(query.OrderByProperty);
}
else
{
result = result.OrderByDescending(query.OrderByProperty);
}
}
result = result.Skip(query.Skip).Take(query.Amount);
return result.ToList();
}
}
Although this is most certainly an anti-pattern, it's a neat way to just look at some data, if that's what you want. It's called very easily like this:
DataQuery query = new DataQuery
{
Amount = int.Parse(txtAmount.Text),
Skip = 0,
FilterByProperty = ddlFilterBy.SelectedValue,
FilterByOperator = ddlOperator.SelectedValue,
FilterCompare = txtCompare.Text,
OrderByProperty = ddlOrderBy.SelectedValue,
Descending = chkDescending.Checked
};
grdData.DataSource = DataService.GetData<Server>(query);
grdData.DataBind();
"Server" is one of the classes/document types I'm working with, so the downside, where it isn't completely dynamic, is that I would have to define a call like that for each type.
I strongly suggest you don't go down this road. You are essentially attempting to hide the RavenDB Session object, which is very powerful and intended to be used directly.
Just looking at the signature of the method you want to create, the parameters are all very restrictive and make a lot of assumptions that might not be true for the data you're working on. And the return type - why would you return a DataTable? Maybe return an object or a dynamic, but nothing in Raven is structured in tables, so DataTable is a bad idea.
To answer the specific question, the type name comes from the Raven-Entity-Name metadata, which you would need to build an index over. This happens automatically when you index using the from docs.YourEntity syntax in an index. Raven does this behind the scenes when you use a dynamic index such as .Query<YourEntity> or .Advanced.LuceneQuery<YourEntity>.
Still, you shouldn't do this.

Categories

Resources