I have a code that fetching a table. Here's my sample code:
public IQueryable<MyItem> MyItems(){
return context.MyItem;
}
Here are the sample properties of MyItem
public int Id {get;set;}
public byte[] Image {get;set;}
public string Name {get;set;}
Since, byte[]
can have multiple characters, I don't want to include then in searching because it will take so long if I have a records like 10,000 items.
Typically, I would Select
like this:
public IQueryable<MyItem> MyItems(){
return context.MyItem.Select(item=>new MyItem{
Id=item.Id,
Name=item.Name
});
}
This is okay for few properties, but what I have a 10-20 properties, it would be hassle to write them one by one.
Is there any way like, I just Except the property Image
in lambda for shorter code?
Create your own extension method SelectExcept
:
public static IQueryable<T> SelectExcept<T, TKey>(this IQueryable<T> sequence,
Expression<Func<T, TKey>> excluder)
{
List<string> excludedProperties = new List<string>();
if (excluder.Body is MemberExpression memberExpression)
{
excludedProperties.Add(memberExpression.Member.Name);
}
else if (excluder.Body is NewExpression anonymousExpression)
{
excludedProperties.AddRange(anonymousExpression.Members.Select(m => m.Name));
}
var includedProperties = typeof(T).GetProperties()
.Where(p => !excludedProperties.Contains(p.Name));
return sequence.Select(x => Selector(x, includedProperties));
}
private static T Selector<T>(T obj, IEnumerable<PropertyInfo> properties)
{
var instance = Activator.CreateInstance<T>();
foreach (var property in properties)
property.SetValue(instance, property.GetValue(obj), null);
return instance;
}
Usage:
var result = context.MyItem.SelectExcept(x => x.Image);
You can exclude more than one property:
var result = context.MyItem.SelectExcept(x => new { x.Image, x.Name });
You can use AutoMapper for this. Just create a DTO class with all the properties excluding those which you don't want to query:
public class MyItemDTO
{
public int Id { get; set; }
public string Name { get; set; }
}
Then add some mapping:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<MyItem, MyItemDTO>();
});
Now you can query your entities like this:
context.MyItem
.OrderBy(m => m.Name)
.ProjectTo<MyItemDTO>();
This extension is available in AutoMapper.QueryableExtensions
.
You can also create a DTO class with all the properties from an entity and then ignore some of them in mapping:
cfg.CreateMap<MyItem, MyItemDTO>().ForMember(d => d.Image, m => m.Ignore());
If you use EF Core you can instantiate entity class inside LINQ query so it's possible to map entity to itself and ignore some properties from it without creating additional DTO classes:
cfg.CreateMap<MyItem, MyItem>().ForMember(d => d.Image, m => m.Ignore());
Then you just use ProjectTo
with the entity type which excludes unwanted properties from SQL query:
context.MyItem
.OrderBy(m => m.Name)
.ProjectTo<MyItem>();
Note that you may also need to create mappings for each navigation property of an entity.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With