Dynamic Sort expression issue - sorting

Let say I have classes hierarchy like
public class A
{
public int AMember { get; set; }
}
public class B
{
public int BMember { get; set; }
public virtual A AasMember { get; set; }
}
public static class OrderByUtility
{
public static bool PropertyExists<T>(string propertyName)
{
return typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public) != null;
}
}
From main class whenever I use this Utility
OrderByUtility.PropertyExists BClass>("BMember");
This works fine and returns TRUE. But whenever I use
OrderByUtility.PropertyExists BClass> ("AMember"); returns False
I want same PropertyExist function work for all Composed Object. Please suggest resolving this issue. Thanks

Here is a really naive implementation of it. You could make it recursive to keep checking nested types. It also keeps a cache so it doesn't do the expensive processing on each lookup.
public class A
{
public int AMember { get; set; }
}
public class B
{
public int BMember { get; set; }
public virtual A AasMember { get; set; }
}
public static class OrderByUtility
{
private static readonly Dictionary<Type, Dictionary<string, bool>> Seen =
new Dictionary<Type, Dictionary<string, bool>>();
public static bool PropertyExists<T>(string propertyName)
{
var type = typeof(T);
if (!Seen.TryGetValue(type, out var props))
{
props = new Dictionary<string, bool>();
Seen[type] = props;
}
if (props.ContainsKey(propertyName))
{
return props[propertyName];
}
var prop = GetPropertyByName(type, propertyName);
if (prop == null)
{
foreach (var p in type.GetProperties())
{
var propType = p.PropertyType;
if (!propType.IsClass && !propType.IsInterface) continue;
prop = GetPropertyByName(propType, propertyName);
if (prop != null)
{
break;
}
}
}
props[propertyName] = prop != null;
return props[propertyName];
}
private static PropertyInfo GetPropertyByName(Type t, string name)
{
return t.GetProperty(name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
}
}
Usage:
bool exists = OrderByUtility.PropertyExists<B>("AMember");

Related

how to apply generic multiple include with generic repository?

I need to make generic multiple include function as a service with generic repository.
But unfortunately, I get nothing !!
here is my attempt using aggregate linq.
public IQueryable<TEntityDTO> getRowsWithIncludeMultiple(int page = 0, params Expression<Func<TEntityDTO, object>>[] includes)
{
GridSetting gs = GetGrid();
IEnumerable<TEntity> getPage = _dbSet.Skip((page == 0 ? page : page - 1) * gs.ItemsPerPage).Take(gs.ItemsPerPage);
IQueryable<TEntityDTO> rows = _mapper.Map<IEnumerable<TEntityDTO>>(getPage).AsQueryable();
if (includes != null) { rows = includes.Aggregate(rows, (current, include) => current.Include(include)); }
// or
//foreach (var include in includes)
//{
// rows = rows.Include(include);
//}
return rows;
}
when I add debugging point I get that the includes has list of expression
and then here is how I use it
var xxx = _customerService.getRowsWithIncludeMultiple(page: 0, i => i.cityDTO, i => i.ageDTO);
the problem here I get customers without the included things (cityDTO & ageDTO)
let me include here models
public class CustomerDTO
{
public int Id { get; set; }
public string CustName { get; set; }
public string CustJobTitle { get; set; }
public string CustAge { get; set; }
public bool IsManager { get; set; }
// FKs
public int AgeId { get; set; }
public int CityId { get; set; }
public AgeDTO ageDTO { get; set; }
public CityDTO cityDTO { get; set; }
}
public class CityDTO
{
public int Id { get; set; }
public string CityName { get; set; }
public List<CustomerDTO> customerDTO { get; set; }
}
public class AgeDTO
{
public int Id { get; set; }
public int AgeName { get; set; }
public List<CustomerDTO> customerDTO { get; set; }
}
Update ... showing the whole service, usage, and injection
here is the whole generic repository service and how it looks like
public class Repository<TEntity, TEntityDTO> : IRepository<TEntity, TEntityDTO> where TEntity : class where TEntityDTO : class
{
protected readonly AppDbContext _context;
private readonly DbSet<TEntity> _dbSet;
private readonly IMapper _mapper;
public Repository(AppDbContext context, IMapper mapper)
{
_context = context;
_dbSet = context.Set<TEntity>();
_mapper = mapper;
}
// GENERIC CRUD ...
// and then here where i want to focus
public IQueryable<TEntityDTO> getRowsWithIncludeMultiple(int page = 0, params Expression<Func<TEntityDTO, object>>[] includes)
{
GridSetting gs = GetGrid();
IEnumerable<TEntity> getPage = _dbSet.Skip((page == 0 ? page : page - 1) * gs.ItemsPerPage).Take(gs.ItemsPerPage);
IQueryable<TEntityDTO> rows = _mapper.Map<IEnumerable<TEntityDTO>>(getPage).AsQueryable();
if (includes != null) { rows = includes.Aggregate(rows, (current, include) => current.Include(include)); }
// or
//foreach (var include in includes)
//{
// rows = rows.Include(include);
//}
return rows;
}
}
and then here is how customer service uses generic repo
public class CustomerService : Repository<Customer, CustomerDTO>, ICustomerService
{
public CustomerService(AppDbContext db, IMapper mapper) : base(db, mapper) { }
}
finally injection in Program.cs
builder.Services.AddScoped(typeof(IRepository<,>), typeof(Repository<,>));
builder.Services.AddScoped<ICustomerService, CustomerService>();
thanks #Svyatoslav Danyliv and thanks for all contributors your answer helped me a lot.
cannot apply Include to DTO object
so, I would to share what I have made so far.
instead of
public IQueryable<TEntityDTO> getRowsWithIncludeMultiple(int page = 0, params Expression<Func<TEntityDTO, object>>[] includes)
{
GridSetting gs = GetGrid();
IEnumerable<TEntity> getPage = _dbSet.Skip((page == 0 ? page : page - 1) * gs.ItemsPerPage).Take(gs.ItemsPerPage);
IQueryable<TEntityDTO> rows = _mapper.Map<IEnumerable<TEntityDTO>>(getPage).AsQueryable();
if (includes != null) { rows = includes.Aggregate(rows, (current, include) => current.Include(include)); }
return rows;
}
I applied include to the original entity not to DTO
public IEnumerable<TEntityDTO> IncludeMultiple(int page = 0, params Expression<Func<TEntity, object>>[] includes)
{
GridSetting gs = GetGrid();
IQueryable<TEntity> rows = _dbSet.Skip((page == 0 ? page : page - 1) * gs.ItemsPerPage).Take(gs.ItemsPerPage).AsQueryable();
if (includes != null)
{ rows = includes.Aggregate(rows, (current, include) => current.Include(include).AsQueryable().AsNoTracking()); }
return _mapper.Map<IEnumerable<TEntityDTO>>(rows).AsQueryable();
}
And here is some clarification to make the idea more understandable
public IEnumerable<TEntityDTO> IncludeMultiple(params Expression<Func<TEntity, object>>[] includes)
{
// get IQueryable of TEntity
IQueryable<TEntity> rows = _dbSet.AsQueryable();
// and here using `linq Aggregate` to append multiple include statement
if (includes != null)
{ rows = includes.Aggregate(rows, (current, include) => current.Include(include).AsQueryable().AsNoTracking()); }
// here I use auto mapper to map IEnumerable object to IEnumerable object as a result
return _mapper.Map<IEnumerable<TEntityDTO>>(rows).AsQueryable();
}
and finally the usage
_ServiceName.IncludeMultiple(i => i.city, i => i.age);

Linq Union do not work

I am trying to use Linq Union to add additional record into result but Union do not work. Maybe someone could point me in right direction.
public class ProductView
{
public int Id { get; set; }
public bool Active { get; set; }
public string Name { get; set; }
public int ProductTypeId { get; set; }
public int UserCount { get; set; }
}
void Main()
{
var product = Products.Select(p => new ProductView
{
Id = p.Id,
Active = p.Active,
Name = p.Name,
ProductTypeId = p.ProductTypeId,
UserCount = 1
}).ToList();
//The new item is not jointed to the result above
product.Union(new[] {
new ProductView
{
Id = 9999,
Active = true,
Name = "Test",
ProductTypeId=0,
}
});
product.Dump();
}
You need to store the output:
var product2 = product.Union(new[] {
new ProductView
{
Id = 9999,
Active = true,
Name = "Test",
ProductTypeId=0,
}
});
product2.Dump();
In addition to this, overriding the Equals behaviour would be useful - as you probably want to check equality using just the Id field?
For example, if you don't override the Equals behaviour, then you will get Object reference equals like this:
void Main()
{
var list = new List<Foo>()
{
new Foo() { Id = 1},
new Foo() { Id = 2},
new Foo() { Id = 3},
};
var list2 = new List<Foo>()
{
new Foo() { Id = 1},
new Foo() { Id = 2},
new Foo() { Id = 3},
};
var query = list.Union(list2);
query.Dump();
}
// Define other methods and classes here
public class Foo
{
public int Id {get;set;}
}
produces six items!
But if you change Foo to:
public class Foo
{
public int Id {get;set;}
public override bool Equals(object obj)
{
if (obj == null || !(obj is Foo)) return false;
var foo= (Foo)obj;
return this.Id == foo.Id;
}
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
}
then you will get 3 items - which is probably what you are expecting.
You need to override Equals and GetHashCode in ProductView if you want to use Union in a meaningful way(other than comparing by refence).
public class ProductView
{
public int Id { get; set; }
public bool Active { get; set; }
public string Name { get; set; }
public int ProductTypeId { get; set; }
public int UserCount { get; set; }
public override bool Equals(object obj)
{
if (obj == null || !(obj is ProductView)) return false;
ProductView pv2 = (ProductView)obj;
return this.Id == pv2.Id;
}
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
}
You could also implement an IEqualityComparer<ProductView> in a similar way and use it for this overload of Union.

knockoutjs with mvc collection model binding

I'm using knockoutjs to render a collection of items. After allowing the user to do some inline editing I need to post the collection back to the server. However, the collection isn't being populated on the server because I'm not using the name="[0].Blah" naming convention. Does anyone know how to either render name attributes like this using knockoutjs OR how to create a model binder that will allow me to extract the values from the ValueProvider?
You can see a screenshot of the ValueProvider during debugging below.
http://i.imgur.com/zSU5Z.png
Here is my managed ViewModel:
public class FundLevelInvestmentUploadResult
{
public string FileName { get; set; }
public IList<FundLevelInvestmentViewModel> Items { get; set; }
public int NumberOfErrors { get; set; }
public bool ShowErrorsOnly { get; set; }
public FundLevelInvestmentUploadResult()
{
Items = new List<FundLevelInvestmentViewModel>();
}
}
Here is the managed class for "Items":
public class FundLevelInvestmentViewModel
{
private string _fund;
private string _fundType;
private string _date;
private string _netOfWaivedFees;
private string _waivedFees;
private string _bcip;
private string _fxRate;
public uint RowIndex { get; set; }
public int? DealCode { get; set; }
public bool DealCodeIsValid { get; set; }
public string Fund
{
get { return _fund; }
set { _fund = GetString(value); }
}
public bool FundIsValid { get; set; }
public string FundType
{
get { return _fundType; }
set { _fundType = GetString(value); }
}
public bool FundTypeIsValid { get; set; }
public string DateOfInvestment
{
get { return _date; }
set { _date = GetString(value); }
}
public bool DateOfInvestmentIsValid { get; set; }
public string NetOfWaivedFees
{
get { return _netOfWaivedFees; }
set { _netOfWaivedFees = GetString(value); }
}
public bool NetOfWaivedFeesIsValid { get; set; }
public string WaivedFee
{
get { return _waivedFees; }
set { _waivedFees = GetString(value); }
}
public bool WaivedFeeIsValid { get; set; }
public string BCIP
{
get { return _bcip; }
set { _bcip = GetString(value); }
}
public bool BCIPIsValid { get; set; }
public string ExchangeRateToUSD
{
get { return _fxRate; }
set { _fxRate = GetString(value); }
}
public bool ExchangeRateToUSDIsValid { get; set; }
public string FileName { get; set; }
private IList<string> _errors;
public IList<string> Errors
{
get { return _errors ?? (_errors = new List<string>());}
set { _errors = value; }
}
public bool Show { get; set; }
public FundLevelInvestmentViewModel()
{
Errors = new List<string>();
Show = true;
}
// knockoutjs is returning "null" instead of "" for a null object when calling ko.mapping.fromJS
private string GetString(string value)
{
if (value == "null")
return string.Empty;
return value;
}
}
Here is my knockout viewModel:
var viewModel = {
FileData: ko.observableArray([]),
validateFile: function (file, event) {
$.ajax({
type: 'post',
url: newUrl,
data: ko.mapping.toJS(file)
}).done(function (data) {
var newFile = ko.mapping.fromJS(data);
var index = file.Items.indexOf(file);
viewModel.FileData.replace(file, newFile);
});
}
};
If you are using version 2.1.0.0 or later of knockout you can render the name attribute as follows from an observable array.
<input data-bind='attr: { name: "Items["+$index()+"].DealCode"}' />

entity framework programming against interface and repository pattern

I am developing in ASP.NET MVC3 and EntityFramework.
I want my model to follow an interface :
public class Account : IAccount
{
public string Id { get; set; }
public DateTime Date { get; set; }
public string Language { get; set; }
}
public interface IAccount
{
string Id { get; set; }
DateTime Date { get; set; }
string Language { get; set; }
}
Here's my Context
public class EFContext : DbContext, IContext
{
public DbSet<Account> Accounts { get; set; }
}
And here's the repository :
public interface IRepository<T> where T : class
{
IQueryable<T> All { get; }
int Count { get; }
bool Contains(Expression<Func<T, bool>> predicate);
void Create(T item);
void Update(T item);
void Delete(Expression<Func<T, bool>> predicate);
void Delete(T item);
}
public class EFRepository<T> : IRepository<T> where T : class
{
private EFContext _context;
public EFRepository(IUnitOfWork uow)
{
this._context = (EFContext)uow.Context;
}
protected DbSet<T> DbSet
{
get
{
return _context.Set<T>();
}
}
public IQueryable<T> All
{
get
{
return DbSet.AsQueryable();
}
}
public virtual int Count
{
get
{
return DbSet.Count();
}
}
public bool Contains(Expression<Func<T, bool>> predicate)
{
return DbSet.Count(predicate) > 0;
}
public virtual void Create(T item)
{
DbSet.Add(item);
}
public virtual void Update(T TObject)
{
var item = DbSet.Attach(TObject);
_context.SetItemState(TObject, EntityState.Modified);
}
public virtual void Delete(Expression<Func<T, bool>> predicate)
{
var objects = DbSet.Where(predicate);
foreach (var obj in objects)
{
DbSet.Remove(obj);
}
}
public virtual void Delete(T TObject)
{
DbSet.Remove(TObject);
}
}
Now, I want to use IRepository<IAccount> but this will ask the context for DbSet<IAccount>. This leads to an error since the Context contains a DbSet<Account>.
I then tried the solution proposed here for Linq2Sql : http://iridescence.no/post/Linq-to-Sql-Programming-Against-an-Interface-and-the-Repository-Pattern.aspx
So I added this function to my EFContext
public new DbSet<T> Set<T>() where T : class
{
var ciccio = TableMaps[typeof(T)];
return (DbSet<T>)base.Set(ciccio).Cast<T>();
}
But it doesn't work.
Do anyone have a suggestion?
Thx
What benefit are you receiving from using an interface for your entities? I don't see any value here. Typically, you use Interfaces to remove dependencies upon the implementation, but that's not what you're achieving here because you're returning a concrete DbSet of objects.
Your entities are already Poco's. They don't have dependencies on other implemntations, and they have no code in them other than a getter/setter. Using an interface is redundant and pointless.
I've found a workaround. I kind of like it so I want to share it.
I rewritten my EFRepository :
public class EFRepository<T, W> :
IRepository<T> where T : class
where W : class, T
{
private EFContext _context;
public EFRepository(IUnitOfWork uow)
{
this._context = (EFContext)uow.Context;
}
protected DbSet<W> DbSet
{
get
{
return _context.Set<W>();
}
}
public IQueryable<T> All
{
get
{
return DbSet.AsQueryable<T>();
}
}
public virtual int Count
{
get
{
return DbSet.Count();
}
}
public bool Contains(Expression<Func<T, bool>> predicate)
{
return All.Count(predicate) > 0;
}
public virtual void Create(T item)
{
DbSet.Add(item as W);
}
public virtual void Update(T TObject)
{
var item = DbSet.Attach(TObject as W);
_context.SetItemState(TObject, EntityState.Modified);
}
public virtual void Delete(Expression<Func<T, bool>> predicate)
{
var objects = All.Where(predicate);
foreach (var obj in objects)
{
DbSet.Remove(obj as W);
}
}
public virtual void Delete(T TObject)
{
DbSet.Remove(TObject as W);
}
}
So now basically all I need to do now is
IRepository<IAccount>> accRepository = new EFRepository<IAccount, Account>(uow);
I am happy with this solution, but still I'm not sure it is the best one, so any comments will be appreciated.
Thanks

Concrete implementation of generic base class and extension method

The end goal for this post is to override the ToString() method of a concrete implementation of a generic base class while still being able to search the implementation using Linq flattening technique. So if you read this and see a better way let me know. I'm using Telerik controls for Silverlight and they won't change their api to allow some of their control properties to be data-bound and instead rely on the ToString() method of whatever object they are bound to. yea, stupid.. Anyway here is what I've got.
RadTreeView control on my page. The FullPath property of each node in the treeview uses the ToString() method of each item its bound to (so this is what I need to override).
I had to create an "intermediary" class to enhance my base model class so it can be bound as a heirarchy in the tree view and then a concrete implementation of that generic class to override ToString(). Now the problem is I have a Linq extension that explodes because it cannot convert the concrete implementation back to the base generic class. I love generics but this is too much for me. Need help on solving the extension method issue.
Intermediary generic class:
public class HeirarchicalItem<T> : NotifyPropertyChangedBase, INotifyCollectionChanged where T : class
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs ea)
{
if (CollectionChanged != null)
CollectionChanged(this, ea);
}
public HeirarchicalItem() { }
public HeirarchicalItem(T item)
{
Item = item;
}
public HeirarchicalItem(IEnumerable<T> collection)
{
CopyFrom(collection);
}
private T _item;
public T Item
{
get
{
return _item;
}
set
{
_item = value;
RaisePropertyChanged<HeirarchicalItem<T>>(a => a.Item);
}
}
private ObservableCollection<HeirarchicalItem<T>> _children = new ObservableCollection<HeirarchicalItem<T>>();
public virtual ObservableCollection<HeirarchicalItem<T>> Children
{
get { return _children; }
set
{
_children = value;
RaisePropertyChanged<HeirarchicalItem<T>>(a => a.Children);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
private void CopyFrom(IEnumerable<T> collection)
{
if ((collection != null))
{
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
HeirarchicalItem<T> newHeirarchicalItem = new HeirarchicalItem<T>(enumerator.Current);
Children.Add(newHeirarchicalItem);
RaisePropertyChanged<HeirarchicalItem<T>>(a => a.Children);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
}
}
}
}
}
Base model class: (data is shuttled to and from WCF Ria service using this class)
public class tbl_Path : EntityBase, IFullPath, IEquatable<tbl_Path>, IEqualityComparer<tbl_Path>
{
public tbl_Path();
public int GetHashCode(tbl_Path obj);
public override string ToString();
public DateTime CreateDate { get; set; }
public short Depth { get; set; }
public string FullPath { get; set; }
public bool IsAuthorized { get; set; }
public bool IsSelected { get; set; }
public string Name { get; set; }
public override IEnumerable<Operation> Operations { get; }
public int? ParentPathID { get; set; }
public int PathID { get; set; }
public Guid SecurityKey { get; set; }
public EntityCollection<tbl_Configuration> tbl_Configuration { get; set; }
public EntityCollection<tbl_Key> tbl_Key { get; set; }
public EntityCollection<tbl_SecurityACL> tbl_SecurityACL { get; set; }
public EntityCollection<tbl_SecurityInheriting> tbl_SecurityInheriting { get; set; }
public EntityCollection<tbl_Variable> tbl_Variable { get; set; }
}
Concrete Implementation so that I can override ToString():
public class HeirarchicalPath : HeirarchicalItem<tbl_Path>
{
public HeirarchicalPath()
{
}
public HeirarchicalPath(tbl_Path item)
: base(item)
{
}
public HeirarchicalPath(IEnumerable<tbl_Path> collection)
: base(collection)
{
}
public override string ToString()
{
return Item.Name; **// we override here so Telerik is happy**
}
}
And finally here is the Linq extension method that explodes during compile time because I introduced a concrete implementation of my generic base class.
public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> fnRecurse)
{
foreach (T item in source)
{
yield return item;
IEnumerable<T> seqRecurse = fnRecurse(item);
if (seqRecurse != null)
{
foreach (T itemRecurse in Traverse(seqRecurse, fnRecurse))
{
yield return itemRecurse;
}
}
}
}
Actual code that is breaking: (x.Children is highlighted with the error)
Cannot implicitly convert type
'System.Collections.ObjectModel.ObservableCollection<HeirarchicalItem<tbl_Path>>' to
'System.Collections.Generic.IEnumerable<HeirarchicalPath>'. An explicit conversion
exists (are you missing a cast?)
HeirarchicalPath currentItem = this.Paths.Traverse(x => x.Children).Where(x => x.Item.FullPath == "$/MyFolder/Hello").FirstOrDefault();
Figured it out. Been working on this all day and minutes after posting the question I resolve it as always.
Just needed to add this bit to my concrete implementation and no more compiler errors.
private ObservableCollection<HeirarchicalPath> _children = new ObservableCollection<HeirarchicalPath>();
public new ObservableCollection<HeirarchicalPath> Children
{
get
{
return _children;
}
set
{
if (value == null)
return;
_children = value;
RaisePropertyChanged<HeirarchicalPath>(a => a.Children);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}

Resources