I'm trying to remove duplicated code and run into an issue here:
I've got five very similar entities (different asset types, e.g. Bonds, Stocks). The methods I'm trying to condense return some statistics about these assets. The statistics are obtained with the help of Linq, the queries are almost identical.
Before, I had five separate methods in my controller (e.g. BondStatistics, StockStatistics). One of these would look like this (db is my database context which has each asset type defined):
public JsonResult BondStatistics()
{
var items = db.Bonds.ToList();
var result = new[]
{
new
{
key = "Bonds",
values = items.Select(i =>
new {
x = i.priceChangeOneDayInEuro,
y = i.priceChangeTotalInEuro,
size = i.TotalValueInEuro,
toolTip = i.Description
}
)
},
};
return Json(result, JsonRequestBehavior.AllowGet);
}
I googled that one way to rewrite these into just one method could be using reflection. However, I thought I could use a dirty shortcut, something like this:
public JsonResult Scatter(string asset)
{
if (asset == "Stocks") { var items = db.Stocks.ToList(); };
if (asset == "Bonds") { var items = db.Bonds.ToList(); };
if (asset == "Futures") { var items = db.Futures.ToList(); };
if (asset == "Options") { var items = db.Options.ToList(); };
if (asset == "Funds") { var items = db.Funds.ToList(); }
var result = new[]
{
new
{
key = asset,
values = items.Select(i =>
new {
x = i.priceChangeOneDayInEuro,
y = i.priceChangeTotalInEuro,
size = i.TotalValueInEuro,
toolTip = i.Description
}
)
},
};
return Json(result, JsonRequestBehavior.AllowGet);
}
This leads to the problem that the type of "items" is not known in the Linq query at design time.
What would be a good way to overcome this problem? Use some totally other pattern, do use reflection or is there an easy fix?
EDIT
As suggested, I created an Interface and let the BaseAsset-class implement it. Then, changing the condensed method to
List<IScatter> items = new List<IScatter>();
if (asset == "Stocks") { items = db.Stocks.ToList<IScatter>(); };
if (asset == "Bonds") { items = db.Bonds.ToList<IScatter>(); };
if (asset == "Futures") { items = db.Futures.ToList<IScatter>(); };
if (asset == "Options") { items = db.Options.ToList<IScatter>(); };
if (asset == "Funds") { items = db.Funds.ToList<IScatter>(); }
works, at design time at last. Thank you very much!
You are putting everything into var, but what exactly is the type of the items you are processing?
If it would be List<Stock> for db.Stocks.ToList(), List<Bond> for db.Bonds.ToList() you can simply define an interface (e.g. IHasPriceInformation) which has the fields you are using in the LINQ query. Then, Let Stock, Bond and others implement this interface (or provide an abstract base implementation of them) and simply run your LINQ Query on a List<IHasPriceInformation>.
Related
This is my json Data
{
"_id":"biking",
"_rev":"AE19EBC7654",
"type":"user",
"body":"My biggest hobby is mountainbiking. The other day...",
"date":"2009/01/30 18:04:11"
}
{
"_id":"biking",
"_rev":"AE19EBC7654",
"type":"testuser",
"body":"My biggest hobby is mountainbiking. The other day...",
"date":"2009/01/30 18:04:11"
}
Here is what is tried so far
var pull = _db.CreatePullReplication(syncGatewayUri);
var push = _db.CreatePushReplication(syncGatewayUri);
_db.SetFilter("byUser", (revision, filterParams) =>
{
var typeParam = filterParams["type"].ToString();
return (typeParam != null) && typeParam.Equals("USer");
});
pull.Filter ="byUser";
i want to get result that contain only type user. But i not able to apply filter.
So if you want to create a filter for Users only, you don't need a param. What you want to do is return true if the document's type is "user".
Here's an example :
var pull = _db.CreatePullReplication(syncGatewayUri);
var push = _db.CreatePushReplication(syncGatewayUri);
_db.SetFilter("byUser", (revision, filterParams) =>
{
//We get the type property
var docType = (string)revision.GetProperty("type");
//We make sure it's a user
return !String.IsNullOrEmpty(docType) && docType.toLowerCase() == "user";
});
pull.Filter ="byUser";
If you want to be more dynamic, you can create a filter byType and specify the "user" type parameter.
Example :
var pull = _db.CreatePullReplication(syncGatewayUri);
var push = _db.CreatePushReplication(syncGatewayUri);
_db.SetFilter("byType", (revision, filterParams) =>
{
var typeParam = filterParams["type"].ToString();
var docType = (string)revision.GetProperty("type");
return (typeParam != null) && !String.isNullOrEmpty(docType) && typeParam.toLowerCase() == docType.toLowerCase();
})
pull.Filter ="byType";
pull.FilterParams = new Dictionary<string, object> { {"type", "user"} };
For more details, see CouchBase documentation.
I am trying to check if an entity in the database has any foreign key relations, so that I can inform the user the entity can or cannot be deleted.
I understand this can be done in a rolled back transaction, however I would like to inform the user how many references and where they are to assist in their decision to delete the entity.
I am trying to avoid loading the entire navigation collection into memory to get this data as it may be large. So, in light of this, I can formulate this simple query to firstly determine if there are any references:
private bool CanDeleteComponent(int compId)
{
var query = _Context.Components.Where(c => c.ComponentID == compId)
.Select(comp => new
{
References = comp.Incidents.Any() &&
comp.Drawings.Any() &&
comp.Documents.Any() &&
comp.Tasks.Any() &&
comp.Images.Any() &&
comp.Instructions.Any()
});
var result = query.FirstOrDefault();
if (result != null)
{
return !result.References;
}
return true;
}
This performs a series of SELECT COUNT(*) FROM <TABLE> WHERE... queries.
Now, I would like to provide some further information on the number of references. Ideally I would like to return a Dictionary with the referenced data's name, and the associated count. This way I can loop through the result, rather than access individual properties of an anonymous type. However, what I have tried results in an exception:
var query = _Context.Components.Where(c => c.ComponentID == compId)
.Select(comp => new Dictionary<string, int>
{
{"Events", comp.Incidents.Count()},
{"Drawings", comp.Drawings.Count()},
{"Documents", comp.Documents.Count()},
{"Tasks", comp.Tasks.Count()},
{"Images", comp.Images.Count()},
{"Instructions", comp.Instructions.Count()},
});
var result = query.FirstOrDefault();
return query.Any(fk => fk.Value > 0);
The exception that is raised is:
A first chance exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll
Additional information: Only list initializer items with a single element are supported in LINQ to Entities.
Is there any way around this, such that I can return some sort of IEnumerable rather than an anonymous type?
Thanks
EDIT
I currently have lazy loading disabled on my context. If there is a solution without turning Lazy loading on that would be appreciated.
You can't build a Dictionary<K,V> in the SELECT statement, that's why you get System.NotSupportedException. You can get the single Component first by query, and build the dictionary in the memory.
var comp = _Context.Components.SingleOrDefault(c => c.ComponentID == compId);
var dict = new Dictionary<string, int>()
{
{ "Events", comp.Incidents.Count()},
{ "Drawings", comp.Drawings.Count()},
{ "Documents", comp.Documents.Count()},
{ "Tasks", comp.Tasks.Count()},
{ "Images", comp.Images.Count()},
{ "Instructions", comp.Instructions.Count()}
};
EDIT If you are not using lazy loading, you can explicitly .Include the properties in the query:
var comp = _Context.Components
.Include(c => c.Incidents)
...
.SingleOrDefault(c => c.ComponentID == compId);
Is there any way around this, such that I can return some sort of IEnumerable rather than an anonymous type?
Actually there is, although I'm not sure you'll like the generated SQL (compared to the one using anonymous type).
var query = _Context.Components.Where(c => c.ComponentID == compId)
.SelectMany(comp => new []
{
new { Key = "Events", Value = comp.Incidents.Count() },
new { Key = "Drawings", Value = comp.Drawings.Count() },
new { Key = "Documents", Value = comp.Documents.Count() },
new { Key = "Tasks", Value = comp.Tasks.Count() },
new { Key = "Images", Value = comp.Images.Count() },
new { Key = "Instructions", Value = comp.Instructions.Count() },
}.ToList());
var result = query.ToDictionary(e => e.Key, e => e.Value);
return query.Any(fk => fk.Value > 0);
I have two list views which have same data but differing in the number of records. I want to get the non-matching listviewitems in third list view. I have using the following code but it is not helping. The variables x and y are making problem.
var list1Source = lvFace.Items.Cast<ListViewItem>();
var list2Source = lvDBdata.Items.Cast<ListViewItem>();
lvDataToUpload = list1Source.Where(
(x => list2Source.All(y => y.Text != x.Text));
You are looking for LINQ Except method
var lvExcept1 = list1Source.Except(list2Source);
var lvExcept2 = list2Source.Except(list1Source);
lvDataToUpload = lvExcept1.Union(lvExcept2);
But you need to override Equals and GetHashCode methods for your ListViewItem class. If there is no option to do this (ListViewItem is Windows Forms class, not yours), you can define your own equality comparer:
public class ListViewItemComparer : IEqualityComparer<ListViewItem>
{
bool IEqualityComparer<ListViewItem>.Equals(ListViewItem x, ListViewItem y)
{
return (x.Text == y.Text);
}
int IEqualityComparer<ListViewItem>.GetHashCode(ListViewItem obj)
{
if (Object.ReferenceEquals(obj, null))
return 0;
return obj.Text.GetHashCode();
}
}
And final code is:
var lvExcept1 = list1Source.Except(list2Source, new ListViewItemComparer());
var lvExcept2 = list2Source.Except(list1Source, new ListViewItemComparer());
lvDataToUpload = lvExcept1.Union(lvExcept2);
LINQ doesn't have a "set difference" operator itself... but you can use Except twice:
var list1Text = list1Source.Select(x => x.Text);
var list2Text = list2Source.Select(x => x.Text);
var difference = list1Text.Except(list2Text)
.Concat(list2Text.Except(list1Text))
.ToList();
Try this
listIntersection = list1Source.Intersect(list2Source); // Gets matching elements
listUnion = list1Source.Union(list2Source); // Gets all elements
lvDataToUpload = listUnion.Except(listIntersection);
I need to make a query that return all items with the current price and the current reduction if any.
I tried a few solutions but none seem to work or respect the patterns as i understand them.
The dynamic solution:
I tried to return the data as a dynamic that would be an IQueryable where T would be (Item, CurrentItemPrice, CurrentItemStateIfAny)
public ItemRepository(CoconutEntities context) : base(context){}
public dynamic GetAllCurrentItems(){
var items = (from item in context.Items
select new {
Item = item,
CurrentItemPrice = item.ItemPrices.Where(x => item.ItemPrices.Max(y => y.EffectiveDate) == x.EffectiveDate),
CurrentItemState = item.ItemReductions.Where(x => x.StartDate <= DateTime.Now && DateTime.Now <= x.EndDate)});
return items;
}
But when i try this and i need to add filter, i can't add them the way i was expecting.
public dynamic GetCurrentItems(string filter = "", int categoryId = 1) {
dynamic result;
var categoryServices = new CategoryServices();
IEnumerable<int> categoryIdAndChildCategoriesId = categoryServices.GetCategoryIdAndChildsId(categoryId);
if (!string.IsNullOrWhiteSpace(filter))
{
result = this.GetAllCurrentItems().Where(x => ((string)(x.Item.Name)) == filter);
}
else if(categoryId != 1)
{
result = this.GetAllCurrentItems().Where(x => x.Item.ItemCategories.Any(x => categoryIdAndChildCategoriesId.Contains(x.CategoryId)));
}
return result;
}
Solution 2 : I also tried with Tuple where i should have been able to do somthing like this but i can't create Tuples from Linq to Entities if i understood in an other post. I would need to query all the item first, then use linq to object to create my tuples.
Solution 3 : I can create a viewmodel or a new model that would represent the data i need. I know this would work but i don't understand where it would stand between the two. If it is not a view model, this information won't go to the view it an other way to see an item with only the current information.
In short, there are probably many solutions to this problem, but i need help to understand which solution would be the best and why.
As I understood you you want to do as much as possible on the database - that is good. You might achieve that with tuples like that:
public IEnumerable<Tuple<Item,decimal, decimal>> GetAllCurrentItems(Expression<Func<Item, bool>> filterExpression){
using(MyContext context = new MyContext())
{
var items = context.Items
.Where(filterExpression)
.Select(item => new Tuple<Item,decimal, decimal> (
item,
item.ItemPrices.Where(x => item.ItemPrices.Max(y => y.EffectiveDate) == x.EffectiveDate),
item.ItemReductions.Where(x => x.StartDate <= DateTime.Now && DateTime.Now <= x.EndDate)});
return items;
}
}
And calling it like that:
public IEnumerable<Tuple<Item,decimal, decimal>> GetCurrentItems(string filter = "", int categoryId = 1) {
dynamic result;
var categoryServices = new CategoryServices();
IEnumerable<int> categoryIdAndChildCategoriesId = categoryServices.GetCategoryIdAndChildsId(categoryId);
if (!string.IsNullOrWhiteSpace(filter))
{
result = this.GetAllCurrentItems(x => ((string)(x.Item.Name)) == filter);
}
else if(categoryId != 1)
{
result = this.GetAllCurrentItems(x => x.Item.ItemCategories.Any(x => categoryIdAndChildCategoriesId.Contains(x.CategoryId)));
}
return result;
}
I have an IQueryable I selected from a database that contains a field called "NewValue". This field will either contain a 0, 1, or 2. I want to replace the value with a string based of which number it is.
i.e 0 = "Active". I know IQueryables aren't used to change values mostly just for querying but I need to use it in the next line to add to the ViewModel
var AssessmentList = assessment
.Select(p => new LogManagementViewModel
{
newValue = p.NewValue,
});
How can I change all the values in assement.NewValue to a string?
var dictionary = new Dictionary<int, string>
{
{ 0, "Active" },
{ 1, "Inactive" },
// etc
};
var AssessmentList = assessment
.Select(p => new LogManagementViewModel
{
newValue = dictionary[p.NewValue],
});
This would work for mapping between other types as well.
var values = new [] {"StrValue1", "StrValue2", "StrValue"};
var AssessmentList = assessment
.AsEnumerable()
.Select(p => new LogManagementViewModel
{
newValue = values[p.NewValue],
});
Easiest and cleanest would be with a mapping method:
string MapToString(int value)
{
//..
}
var AssessmentList = assessment
.Select(p => new LogManagementViewModel
{
NewValue = MapToString(p.NewValue),
});
One way:
Add a new computed property onto your class (assuming you have a partial class outside of any generated code). Then that computed class can do the translation however it needs to. Your view model can reference this computed value.
I would make a constructor overload in your LogManagementViewModel that does that work for you, something like this:
public LogManagementViewModel(int NewValue)
{
switch(NewValue)
{
case 0:
this.NewValue = "Active";
break;
// etc...
}
}
IMO, this places the logic in it's proper place, let the your view model translate it as it should be, it's not the responsibility of your LINQ/Database queries.