How to Generic implement of the following query
double sum=context.Employees.Where(e=>(e.Id==12 && e.Sl==100)).Sum(e=>e.Salary);
i am using a repository system.
i want something like this
T total=GetTotalSalary((e.Id==12 && e.Sl==100),e.Salary);
Just pass the variables as param to the function and use them in linq
public double GetTotalSalary(int empId, double empSalary)
{
return context.Employees.Where(e=>(e.Id==empId && e.Sl==empSalary)).Sum(e=>e.Salary);
}
Without knowing if 12 and 100 is hardcoded, this could be a good starting point:
Updated to reflect comments:
public abstract class Contract
{
public decimal Salery { get; set; }
}
public class EmployeePermanent : Contract {
}
public class Employee : Contract
{
public int Id { get; set; }
public int Sl { get; set; }
public void Test()
{
new List<Employee>().AsQueryable().GetTotalSalary(x => x.Id == 12 && x.Sl == 100);
}
}
public static class QueryExtensions
{
public static decimal GetTotalSalary<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> expression) where T : Contract
{
return queryable.Where(expression).Sum(arg => arg.Salery);
}
}
Related
We want to convert a List<Rule> where we know they all derive from BuildingRule<T> into BuildingRule<T>, but the type T can be different.
We basically want to do
_buildingRules.Find(x => ((BuildingRule<T>)x).GetBuilding(sku, upgradeId) != null)
(the `.GetBuilding() != null is a requirement the rule has to meet in order for it to be the right one).
Though this does not work, and we were wondering if there is a way to achieve what we are trying to do.
public abstract class Rule
{
[XmlIgnore]
public abstract string FileName { get; }
}
public abstract class BuildingRule<T> : Rule where T : BuildingDefinitionRule
{
[XmlElement("Definition")]
public virtual List<T> Definitions { get; set; }
public virtual T GetBuilding(string sku, int upgradeId = 0)
{
return Definitions.Find(x => x.Sku == sku && x.UpgradeId == upgradeId);
}
}
public abstract class BuildingRule : BuildingRule<BuildingDefinitionRule>
{
}```
You can create non-generic BuildingRuleBase:
public abstract class BuildingRuleBase : Rule
{
public abstract BuildingDefinitionRule GetBuilding(string sku, int upgradeId = 0);
}
And inherit it:
public abstract class BuildingRule<T> : BuildingRuleBase where T : BuildingDefinitionRule
{
public override T GetBuilding(string sku, int upgradeId = 0)
{
return Definitions.Find(x => true);
}
}
And make _buildingRules a collection of BuildingRuleBase.
Or look into using covariant generic interfaces:
public interface IBuildingRule<out T> where T : BuildingDefinitionRule
{
T GetBuilding(string sku, int upgradeId = 0);
}
public abstract class BuildingRule<T> : Rule, IBuildingRule<T> where T : BuildingDefinitionRule
{
[XmlElement("Definition")]
public virtual List<T> Definitions { get; set; }
public T GetBuilding(string sku, int upgradeId = 0)
{
return Definitions.Find(x =>true);
}
}
With new List<IBuildingRule<BuildingDefinitionRule>>() for type of _buildingRules.
I am relatively new to EF and I am still in learning process. I have a repository created from which I get the data from the database.
I have the following structure of classes:
public class Foo1 : CQA, IScorable
{
private double score;
public double Score
{
get
{
return score;
}
set
{
var t = Question; // this is where Question is always null
score = value;
}
}
protected Foo1() { }
public Foo1(CQ q, QO co) : base(question, new List<AO>())
{
}
}
public class CQA : Answer
{
public virtual IList<AO> AO { get; set; }
protected CQA() { }
public CQA(Question question, IList<AO> ao) : base(question)
{
AO = ao;
}
}
public class Answer : IEntity<int>
{
public virtual Question Question { get; set; } // This is null
public int Id { get; set; }
protected Answer() { }
public Answer(Question question)
{
Question = question;
}
}
Mapped table is:
public class AnswerMap : EntityTypeConfiguration<Answer>
{
public AnswerMap()
{
ToTable(nameof(Answer));
HasRequired(x => x.Question);
}
}
This is the class that I get from the repo:
public abstract class Test : IEntity<int>
{
public int Id { get; set; }
public virtual IList<Answer> Answers { get; set; }
protected Test()
{
Answers = new List<Answer>();
}
public virtual Test AddAnswer(Answer answer)
{
var found = Answers.FirstOrDefault(l => l.Question.Id == answer.Question.Id);
if (found != null)
{
var index = Answers.IndexOf(found);
Answers.RemoveAt(index);
Answers.Insert(index, answer);
}
else
{
Answers.Add(answer);
}
return this;
}
}
Answers are always null.
The strangest thing is I am always able to save the answer added for the fist time. Every other time I pull test from repo and AddAnswer method is called I get null in above mentioned places. Calling repo.SaveChanges method works only for the first time and data is inserted in database but after that errors occur.
Can anyone help what am I doing wrong?
The repository that I am using gets all the necessary data expect on lines above where I wrote that problem occurs.
EDIT 1:
Here is how I get the data from repo:
public interface IPSTRepository : IRepository<PTS>
{
}
And here is IRepository interface:
public interface IRepository<TObject> where TObject : class
{
ICollection<TObject> GetAll();
Task<ICollection<TObject>> GetAllAsync();
TObject Get(int id);
Task<TObject> GetAsync(int id);
TObject Find(Expression<Func<TObject, bool>> match);
Task<TObject> FindAsync(Expression<Func<TObject, bool>> match);
ICollection<TObject> FindAll(Expression<Func<TObject, bool>> match);
Task<ICollection<TObject>> FindAllAsync(Expression<Func<TObject, bool>> match);
TObject Add(TObject t);
TObject Update(TObject updated, int key);
void Delete(TObject t);
int Count();
Task<int> CountAsync();
int SaveChanges();
Task<int> SaveChangesAsync();
}
I have a model class with two properties of different types - SourceData, DestinationData. These properties holds instances of similar classes (in different namespaces) that I need to compare manually and display differences.
I would like to create generic extension method IsSame for this problem, so I could write simply:
// SourceData: x => ..., DestinatinData: y => ...
var nameIsSame = model.IsSame(x => x.Name, y => y.Name);
var ageIsSame = model.IsSame(x => x.Age, y => y.Age);
...
I prepared sample with this scenario but I can't write the method IsSame that could be called simply like above. I have to specify all type for generic method call.
public interface ISource
{ }
public interface IDestination
{ }
public class SourcePerson : ISource
{
public string Name { get; set; }
public int Age { get; set; }
}
public class DestinationPerson : IDestination
{
public string Name { get; set; }
public int Age { get; set; }
}
public abstract class DetailViewModel<TSource, TDestination>
where TSource : ISource
where TDestination : IDestination
{
public TSource SourceData { get; set; }
public TDestination DestinationData { get; set; }
}
public class PersonDetailViewModel : DetailViewModel<SourcePerson, DestinationPerson>
{ }
Now I have model class of type PersonDetailViewModel that implements abstract class DetailViewModel<>. I thought that following implementation of method ExtensionMethods.IsSame should be called simply by giving instance of PersonDetailViewModel that might be recognized because of constraints.
public static class ExtensionMethods
{
public static bool IsSame<TModel, TSource, TDestination, TValue>(this TModel model, Func<TSource, TValue> sourceProperty, Func<TDestination, TValue> destinationProperty)
where TModel : DetailViewModel<TSource, TDestination>
where TSource : ISource
where TDestination : IDestination
{
if (model.SourceData == null) {
return (model.DestinationData == null);
}
if (model.DestinationData == null)
return false;
return Equals(sourceProperty(model.SourceData), destinationProperty(model.DestinationData));
}
}
However when I try to call the extension method I have to specify all the types even though the compiler should know then all.
class Program
{
static void Main(string[] args)
{
var model = new PersonDetailViewModel
{
SourceData = new SourcePerson { Name = "Karel Gott", Age = 72 },
DestinationData = new DestinationPerson { Name = "Karel Engles", Age = 72 }
};
Console.WriteLine(model);
Console.WriteLine();
//var nameIsSame = model.IsSame(x => x.Name, y => y.Name); // doesn't work :-(
var nameIsSame = model.IsSame<PersonDetailViewModel, SourcePerson, DestinationPerson, string>(x => x.Name, y => y.Name);
Console.WriteLine("Name is same: " + nameIsSame);
//var ageIsSame = model.IsSame(x => x.Age, y => y.Age); // doesn't work :-(
var ageIsSame = model.IsSame<PersonDetailViewModel, SourcePerson, DestinationPerson, int>(x => x.Age, y => y.Age);
Console.WriteLine("Age is same: " + ageIsSame);
Console.WriteLine();
Console.WriteLine("Press any key to exit ...");
Console.ReadKey(true);
}
}
Notice for all: Please don't write me, that I should compare equality differently. I used here method IsSame just for simplicity. The method works well and the code inside should do something else. I just need to call it without explicitly defined types. In my opinion the compiler should know them all from constrains.
In this scenario I used classes PersonDetailViewModel, SourcePerson, DestinationPerson ... but in my application there are plenty of classes like this. The TSource and TDestination doesnt have same code inside, and the properties should have different names. Imagine that I want to compare properties in these classes:
public class SourceCompany : ISource {
public int CompanyId { get; set; }
public string NameTradeRegister { get; set; }
public string AddressStreet { get; set; }
public string Town { get; set; }
}
public class DestinationCompany : IDestination {
public int ID { get; set; }
public string Name { get; set; }
public string StreetName { get; set; }
public string City { get; set; }
}
var idIsSame = model.IsSame(x => CompanyId, y => y.ID);
Ok, I found the solution. The extension method should avoid TModel and instead it can declare DetailViewModel.
public static class ExtensionMethods
{
public static bool IsSame<TSource, TDestination, TValue>(this DetailViewModel<TSource, TDestination> model, Func<TSource, TValue> sourceProperty, Func<TDestination, TValue> destinationProperty)
where TSource : ISource
where TDestination : IDestination
{
if (model.SourceData == null) {
return (model.DestinationData == null);
}
if (model.DestinationData == null)
return false;
return Equals(sourceProperty(model.SourceData), destinationProperty(model.DestinationData));
}
}
This is an enhancement on my previous question on specification pattern - How to combine conditions dynamically?.
I am trying to make the OnSaleSpecificationForBook method a generic one. The reason being the AudioCD logic also needs a similar specification and both Book and AudioCD implements ISellingItem interface.
Specification
public class OnSaleSpecificationForBook : Specification<Book>
{
public override bool IsSatisfiedBy(Book product)
{
return product.IsOnSale;
}
}
I tried to create a generic method as listed below but it throws following error:
The type or namespace name 'T' could not be found
Code with compilation error
public class OnSaleSpecification : Specification<T>
{
public override bool IsSatisfiedBy(T item)
{
return item.IsOnSale;
}
}
QUESTIONS
What is the reason for this error?
How can we make this method generic?
Note: I am using .Net 4.0. However I would like to know if there is any difference needed when compared with .Net 2.0
Abstractions
public interface ISellingItem
{
bool IsOnSale { get; set; }
double Price { get; set; }
}
public abstract class Specification<T>
{
public abstract bool IsSatisfiedBy(T obj);
}
Client
class Program
{
static void Main(string[] args)
{
List<Book> list = new List<Book>();
Book p1 = new Book(false, 99);
Book p2 = new Book(true, 99);
Book p3 = new Book(true, 101);
list.Add(p1);
list.Add(p2);
list.Add(p3);
var specification = new OnSaleSpecificationForBook();
List<Book> selectedList =
ProductFilterHelper.GetProductsUisngDynamicFilters(list, specification);
}
}
public static class ProductFilterHelper
{
public static List<Book> GetProductsUisngDynamicFilters(List<Book> productList, Specification<Book> productSpecification)
{
return productList.Where(p => productSpecification.IsSatisfiedBy(p))
.ToList();
}
}
Entities
public class Book : ISellingItem
{
public bool IsOnSale { get; set; }
public double Price { get; set; }
public Book(bool isOnSale, double price)
{
this.Price = price;
this.IsOnSale = isOnSale;
}
}
public class AudioCD : ISellingItem
{
public bool IsOnSale { get; set; }
public double Price { get; set; }
public AudioCD(bool isOnSale, double price)
{
this.Price = price;
this.IsOnSale = isOnSale;
}
}
You need to specify what the generic parameter's type is implementing before the compiler will know that it is an ISellingItem. You can do this with a where T: ISellingItem clause:
public class OnSaleSpecification<T> : Specification<T> where T : ISellingItem
{
public override bool IsSatisfiedBy(T item)
{
return item.IsOnSale;
}
}
Your class OnSaleSpecification need to define the generic parameter T and constrain it to an ISellingItem
public class OnSaleSpecification<T> : Specification<T> where T : ISellingItem
{
public override bool IsSatisfiedBy(T item)
{
return item.IsOnSale;
}
}
Updated example to show more general usage.
I have an entity to allow user-provided localization:
public class ResourceValue
{
public int ResourceValueId { get; set; }
public string EnglishValue { get; set; }
public string FrenchValue { get; set; }
public string SpanishValue { get; set; }
etc...
}
Used on many other entities like this:
public class SomeEntity
{
public int Id { get; set; }
public virtual ResourceValue Name { get; set; }
public virtual ResourceValue ShortDescription { get; set; }
public virtual ResourceValue LongDescription { get; set; }
etc...
}
I would like to do something like this:
return context.SomeEntities.OrderBy(x => x.Name);
And have that work as if I had done this:
return context.SomeEntities.OrderBy(x => x.Name.FrenchValue);
based on the CurrentUICulture being "fr-CA".
I have been trying some things based on Marc Gravell's answer here: https://stackoverflow.com/a/1231941 but haven't been able to get quite what I want.
Update - This is pretty close, but I would rather have it be named just "OrderBy" so the end-coder can use it without special consideration:
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
{
return ApplyLocalizedOrder(source, keySelector, "OrderBy");
}
public static IOrderedQueryable<TSource> ApplyLocalizedOrder<TSource, TKey>(IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, string methodName)
{
ParameterExpression arg = keySelector.Parameters[0];
Expression expr = Expression.PropertyOrField(keySelector.Body, GetCurrentCulture());
LambdaExpression lambda = Expression.Lambda<Func<TSource, string>>(expr, arg);
return (IOrderedQueryable<TSource>)typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(TSource), expr.Type)
.Invoke(null, new object[] { source, lambda });
}
While creating lambda expressions dynamically is cool you can achieve your result in a much simpler way just by creating a method that will apply the sort on top of your query. The method would just look like this:
private static IQueryable<SomeEntity> OrderByName(IQueryable<SomeEntity> source, string culture)
{
if (culture == "fr-CA")
{
return source.OrderBy(x => x.Name.FrenchValue);
}
return source.OrderBy(x => x.Name.EnglishValue);
}
And then you would just use it as follows:
OrderByName(context.SomeEntities, "en-US")
Here is the entire example:
public class MyCtx1 : DbContext
{
public DbSet<SomeEntity> SomeEntities { get; set; }
public DbSet<ResourceValue> ResourceValues { get; set; }
}
public class SomeEntity
{
public int Id { get; set; }
public virtual ResourceValue Name { get; set; }
}
public class ResourceValue
{
public int ResourceValueId { get; set; }
public string EnglishValue { get; set; }
public string FrenchValue { get; set; }
}
class Program
{
private static IQueryable<SomeEntity> OrderByName(IQueryable<SomeEntity> source, string culture)
{
if (culture == "fr-CA")
{
return source.OrderBy(x => x.Name.FrenchValue);
}
return source.OrderBy(x => x.Name.EnglishValue);
}
static void Main(string[] args)
{
using (var context = new MyCtx1())
{
if (!context.SomeEntities.Any())
{
context.SomeEntities.Add(
new SomeEntity()
{
Name = new ResourceValue()
{
EnglishValue = "abc - en",
FrenchValue = "xyz - fr"
}
});
context.SomeEntities.Add(
new SomeEntity()
{
Name = new ResourceValue()
{
EnglishValue = "xyz - en",
FrenchValue = "abc - fr"
}
});
context.SaveChanges();
}
Console.WriteLine("Ordered by english name");
DisplayResults(OrderByName(context.SomeEntities, "en-US"));
Console.WriteLine("Ordered by french name");
DisplayResults(OrderByName(context.SomeEntities, "fr-CA"));
}
}
private static void DisplayResults(IQueryable<SomeEntity> q)
{
foreach (var e in q)
{
Console.WriteLine(e.Id);
}
}
And the result:
Ordered by english name
1
2
Ordered by french name
2
1
Press any key to continue . . .
context.SomeEntities.Select(v => v.Name.FrenchName).OrderBy(x => x);
But even better than that in your get for name, return the current culture or a certain culture or whatever your code is calling for, no reason to do it in the Linq query when your class could do it. when it done in the class it is better anyway because then anywhere you call the code it is returning the correct culture.