How to reuse data mapping Expression as a compiled Expression? - c#

I have this code in LinqPad:
void Main() {
var a = Product.Select( DDL.ToDDL).Take(1).Dump();
}
That successfully queries SQL for two table fields instead of all of them using this POCO class (not sure if the inner static makes the outer class static too):
public class DDL {
public static readonly Expression<Func<Product, DDL>> ToDDL =
o => new DDL {
id = o.identifier,
name = o.pbfeattext
};
public int id {get;set;}
public string name {get;set;}
}
So I'd like to write the first line of code this way:
var a = Product.Select(o => o.AsDDL()).Take(1).Dump();
I've tried writing this code:
public static class DDL3 {
public static DDL AsDDL (this Product p) {
return new DDL {
id = p.identifier,
name = p.pbfeattext
};
}
}
Which produces the same result, but it retrieves the entire Product record in SQL instead of just the two fields we need. I've also tried this code, but it requires doing var a = Product.AsDDL().Take(1).Dump();. I need it in the other form (o => o.AsDDL()). (I've even tried using a constructor function, but SQL doesn't understand the function in that form.)
public static class DDL3
{
public static DDL AsDDL (this Product p)
{
return new DDL {
id = p.identifier,
name = p.pbfeattext
};
}
}
How would one code the Linq Expression?

AutoMapper might be for you. see this: https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions
It requires to configure mapping once, but after that, you can reuse DTO mapping anytime.
Here's an example from it:
public List<OrderLineDTO> GetLinesForOrder(int orderId)
{
Mapper.CreateMap<OrderLine, OrderLineDTO>()
.ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name);
using (var context = new orderEntities())
{
return context.OrderLines.Where(ol => ol.OrderId == orderId)
.Project().To<OrderLineDTO>().ToList();
}
}

This is what worked for me. I define the Expression as above, then a compile on the Expression right below it. This is really a View Model taking a POCO and mapping it to itself. You can add attributes to the methods so that JSON.NET doesn't serialize them (assuming...) The reason for Map and ToMap is the first is used to project fields in Entity Framework for example, and the other is used on in-ram collections.
public class DDL {
public static readonly Expression<Func<Product, DDL>> Map =
o => new DDL {
id = o.identifier,
name = o.pbfeattext
};
public static readonly Func<Product, DDL> ToMap =
DDL.Map.Compile();
public int id {get;set;}
public string name {get;set;}
}

Related

How can i select generic columns from a table but make sure my keycolumn is selected too?

I have a table in my database with a lot of columns. I want to have a class where i load columns specified in the constructor into a list. I dont want to load all columns because that takes too long. Additionaly i may want to apply functions on specific columns becuase some data needs to be sanitized. Later i want to be able to return rows from this list by a keycolumn that is fixed (no need to specify it in the constructor).
This is kinda what i want:
public class DataHolder<TType> where TType:class
{
private List<TType> _data;
public DataHolder(DataContext context,Expression<Func<MyTable, TType>> select)
{
_data = context.MyTable.Select(select).DoSanitation().ToList();
//do sanitation on a column if it is in _data here
}
public TType Get(int id)
{
return _data.Single(d => d.Id == id);
}
}
And then i want to use it kinda like this:
var datHolder = new DataHolder(context, x=> new{x.Column1,x.Column2});
var row= datHolder.Get(123);
And row should have the fields "Column1" and "Column2" and "Id".
So i tried it by using anonymous types but because anonymous types cant use interfaces i am not able to make sure the type has the field "Id". Also the whole sanitation thing doesnt make sense on a anonymous type.
I have the sense that i am doing something i should not do or am not seeing a simple solution. I also had a look into Ado.Net which seems like it solve my problems because i can assemble columns adhoc. But all my other code runs with ef core so i am not sure if i should proceed in that direction.
You can't do this with anonymous types, but with types, known at compile time, you can do something like this:
public interface IEntity
{
public int Id { get; }
}
public class DataHolder<TType>
where TType : class, IEntity
{
private static readonly Lazy<IEnumerable<PropertyInfo>> MyTableProperties = new Lazy<IEnumerable<PropertyInfo>>(() => GetPublicInstanceProperties<MyTable>());
private static readonly Lazy<Expression<Func<MyTable, TType>>> Selector = new Lazy<Expression<Func<MyTable, TType>>>(GetSelector);
private readonly IReadOnlyDictionary<int, TType> data;
public DataHolder(MyContext context, Action<TType> doSanitation)
{
var entities = context.MyTable
.Select(Selector.Value)
.ToList();
foreach (var entity in entities)
{
doSanitation(entity);
}
data = entities.ToDictionary(_ => _.Id);
}
public TType Get(int id) => data[id];
private static Expression<Func<MyTable, TType>> GetSelector()
{
var lambdaParameter = Expression.Parameter(typeof(MyTable));
var memberBindings = GetPublicInstanceProperties<TType>()
.Select(propertyInfo => Expression.Bind(propertyInfo, Expression.MakeMemberAccess(lambdaParameter, MyTableProperties.Value.FirstOrDefault(p => p.Name == propertyInfo.Name))));
var memberInit = Expression.MemberInit(Expression.New(typeof(TType)), memberBindings);
return Expression.Lambda<Func<MyTable, TType>>(memberInit, lambdaParameter);
}
private static IEnumerable<PropertyInfo> GetPublicInstanceProperties<T>() => typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
}
GetSelector method is just a simple mapper: it assigns property of TType object from the property of MyTable object with the same name.
Usage:
using (var context = new MyContext())
{
var dataHolder = new DataHolder<EntityA>(context, entity =>
{
// TODO:
});
var row = dataHolder.Get(1);
}
where EntityA is:
public class EntityA : IEntity
{
public int Id { get; set; }
public int A { get; set; }
}

Combining Linq-Entity statements

I have 2 repositories, Member and Person. The Person Model contains a nullable reference property to a Member Model. The Member Model is defined in the Member Repository and I would like to put in place a pattern that ensures this stays that way. however, when I call the methods in the member repo from the person repo I get the
LINQ to Entities does not recognize the method 'System.Linq.IQueryable 1[...IMember] Get(..Entities,System.Linq.Expressions.Expression 1[System.Func`2[..tblMember,System.Boolean]])' method, and this method cannot be translated into a store expression."
While I understand this can be easily solved by putting .asEnumerable() then a second select there is a cost that means your doing 2 queries instead of one and you loose the ability to expose the method as awaitable. Below is the code I have removed a few non essential parts to clearify. I am not using lambda because I have not found a good way to say let in a lambda expression. My goal is person.Member would simply be a nested select. Also Note that the Member side of things is a very ugly database that I have not control over and these repos are being separated for a reason. Thanks in advance
public class MemberRepository : Interfaces.IRepository<IMemberBase, string>
{
private Data.LSAEntities entities { get; set; }
public MemberRepository(Data.LSAEntities entities)
{
this.entities = entities;
}
internal static IQueryable<IMember> Get(Data.LSAEntities entities, Expression<Func<tblMember, bool>> predicate)
{
return (from t in entities.tblMembers.Where(predicate)
let options = entities.tblDataOptions.Where(o => o.DataName == "MemberStatus")
select new Member()
{
MemberID = t.MemberID,
...
});
}
}
public class PersonRepository : IRepository<IPerson, int>
{
private Data.LSAEntities entities { get; set; }
public PersonRepository(Data.LSAEntities entities)
{
this.entities = entities;
}
public IQueryable<IPerson> Get(int key)
{
return (from p in entities.tblPersons
where p.PersonId == key
select new Person()
{
PersonId = p.PersonId,
...
Member = MemberRepository.Get(entities, m=> p.MemberId == m.MemberID).FirstOrDefault()
});
}
}
This may help you:
Given
var ans = from a in table
let b = a.TotalPrice / a.Quantity
where b > 500
select new {
PriceEa = b,
a.ID,
a.Description
};
translates to
var ans = table.Select(a => new { b = a.TotalPrice / a.Quantity, a })
.Where(ba => ba.b > 500)
.Select(ba => new {
PriceEa = ba.b,
ba.a.ID,
ba.a.Description
});
the let clause in query comprehension syntax in LINQ is translated to a Select adding a new field to hold the let value when using lambda syntax.

Use anonymous type in generic class

I have the following class with an Add mapper
public class OrderMapper<T> {
public OrderMapper<T> Add<TKey>(Expression<Func<T, TKey>> expression, String name) {
}
}
which I use as follows:
OrderMapper<Post> mapper = new OrderMapper<Post>().Add(x => x.Created, "created");
The problem is that sometimes I have the following:
posts.SelectMany(x => x.Category, (Post, Category) => new { Post, Category });
Which returns a list of anonymous types:
var objects = new { Post post, Category category }
I am not sure how to create an OrderMapper of this type ...
I think maybe I need to change my class method to an extension like:
OrderMapper mapper = objects.Add(...).Add(...)
Could someone tell me the best way to do this?
You can use a Create method for the OrderMapper class :
public class OrderMapper
{
public static OrderMapper<T> CreateMapperFor<T>(IEnumerable<T> typeProvider)
{
return new OrderMapper<T>();
}
}
And, use it as such :
var objects = Enumerable.Repeat(new { /* ... */ }, 1);
var mapper = OrderMapper.CreateMapperFor(objects)
.Add(/*...*/)
.Add(/*...*/);

generic class for linq / lambda expression

C# Entity framework 4.0
I have a database with 10's of table with 2 common columns 'id' and 'modstamp'
to access modstamp in a table I have a function
protected internal override string GetModStampinChild(int sid)
{
DBContext sq = new DBContext();
return sq.xxxx.Where(s => s.id == sid)
.Select(s => s.modstamp).SingleOrDefault().ToModStampString();
}
where xxxx change for every table.
I am presently overriding this function for every table.
Is there a way to use some kind of generic "class" which I could use where "xxxx" would be any table?
First, you would need to have all of your Entities implement either an interface or an abstract class that contains both the ID and ModStamp properties in it, let's call it Stampable:
public abstract class Stampable
{
[Key]
public int ID { get; set; }
[Required]
public string ModStamp { get; set; }
}
At that point, all you need to do for your method is to have it implement generic typing:
protected internal override string GetModStampInChild<T>(int sid) where T : Stampable
{
using (var sq = new DbContext())
{
return sq.Set<T>.Where(s => s.id == sid)
.Select(s => s.modstamp)
.SingleOrDefault()
.ToModStampString();
}
}
If I understand you correctly, you need a property Set<T> of DbContext class:
First, create base class of all your entity classes with id and modstamp properties. Then:
protected internal override string GetModStampInChild<T>(int sid) where T : BaseEntity
{
using (var sq = new DbContext())
{
return sq.Set<T>.Where(s => s.id == sid)
.Select(s => s.modstamp)
.SingleOrDefault()
.ToModStampString();
}
}
But you must use code-first paradigm for this method.
Another option would be add a new Property to your entity class via the partial class feature of c#.
So the entity definition generated might look like this, note I have no idea what the actual DataType of your ModStamp column is:
public partial class Company
{
public int Id { get; set; }
public byte[] ModStamp { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
}
Note the ModStamp column that you want to convert.
Then add to the Partial.cs file that EF creates code like this, note I have no idea what you actually want to do with the ModStamp value:
public static class ModConverter
{
public static string ToModStampString(byte[] modStamp)
{
return BitConverter.ToString(modStamp);
}
}
public partial class Company
{
public string ModStampString
{
get
{
return ModConverter.ToModStampString(this.ModStamp);
}
}
}
You would then have to manually add a new ModStampString Get Property for every Entity with a ModStamp Column like I did for the Company Entity.
Here is a solution that uses the Set method on the DbContext and expression trees to dynamically query that object.
private Expression<Func<TArg, bool>> CreatePredicate<TArg, TPredicateField>(string fieldName, TPredicateField value)
{
ParameterExpression parameter = Expression.Parameter(typeof(TArg), "o");
MemberExpression memberExpression = Expression.Property(parameter, fieldName);
var condition = Expression.Equal(memberExpression, Expression.Constant(value));
var lambda = Expression.Lambda<Func<TArg, bool>>(condition, parameter);
return lambda;
}
private Expression<Func<TArg, TPredicateField>> CreateSelector<TArg, TPredicateField>(string fieldName)
{
ParameterExpression parameter = Expression.Parameter(typeof(TArg), "o");
Expression propertyExpr = Expression.Property(parameter, fieldName);
var lambda = Expression.Lambda<Func<TArg, TPredicateField>>(propertyExpr, parameter);
return lambda;
}
public TSelectorField GetModStamp<TEntity, TPredicateField, TSelectorField>(TPredicateField id) where TEntity : class
{
using (var ctx = new OnTheFlyEntities("Data Source=(local);Initial Catalog=AscensionBO;Integrated Security=True;MultipleActiveResultSets=True"))
{
var predicate = CreatePredicate<TEntity, TPredicateField>("Id", id);
var selector = CreateSelector<TEntity, TSelectorField>("ModStamp");
TSelectorField item = ctx.Set<TEntity>().Where(predicate).Select(selector).SingleOrDefault();
return item;
}
}
You can then call it like this:
GetModStamp<Entity2, int, string>(1)
If you were willing to just return the found entity, you could eliminate the TSelectorField and then grab the ModStamp from the item after it is retrieved. That will drop one of the expression tree methods and a generic input on the main method.
As someone else suggested, you could go the interface route and use that example, it will be much simpler.

Accessing C# property name or attributes

I would like to automatically generate SQL statements from a class instance. The method should look like Update(object[] Properties, object PrimaryKeyProperty). The method is part of an instance (class, base method - generic for any child). Array of properties is an array of class properties, that will be used in update statement. Property names are equal to table field names.
The problem is that I can't get property names.
Is there any option to get a property name inside class instance?
sample:
public class MyClass {
public int iMyProperty { get; set; }
public string cMyProperty2 { get; set; }
{
main() {
MyClass _main = new MyClass();
_main.iMyProperty.*PropertyName* // should return string "iMyProperty"
{
I am aware of PropertyInfo, but I don't know hot to get the ID of a property from GetProperties() array.
Any suggestion?
Just wrote an implementation of this for a presentation on lambdas for our usergroup last Tuesday.
You can do
MembersOf<Animal>.GetName(x => x.Status)
Or
var a = new Animal()
a.MemberName(x => x.Status)
the code:
public static class MembersOf<T> {
public static string GetName<R>(Expression<Func<T,R>> expr) {
var node = expr.Body as MemberExpression;
if (object.ReferenceEquals(null, node))
throw new InvalidOperationException("Expression must be of member access");
return node.Member.Name;
}
}
Link to the presentation and code samples.
Also in SVN (more likely to be updated): http://gim-projects.googlecode.com/svn/presentations/CantDanceTheLambda
I found a perfect solution in This Post
public static string GetPropertyName<T>(Expression<Func<T>> propertyExpression)
{
return (propertyExpression.Body as MemberExpression).Member.Name;
}
And then for the usage :
var propertyName = GetPropertyName(
() => myObject.AProperty); // returns "AProperty"
Works like a charm
You can do something like this:
Type t = someInstance.getType();
foreach (MemberInfo mi in t.GetMembers())
{
if (mi.MemberType == MemberTypes.Property)
{
Console.WriteLine(mi.Name);
}
}
to get all the property names for instance's type.
You can get the name (I assume that's what you meant by ID) of a property using PropertyInfo.Name. Just loop through the PropertyInfo[] returned from typeof(className).GetProperties()
foreach (PropertyInfo info in typeof(MyClass).GetProperties())
{
string name = info.Name;
// use name here
}
Since you already have an explicit handle to the specific property you want, you know the name - can you just type it?
Not 100% sure if this will get you what you're looking for, this will fetch all properties with [Column] attribute inside your class:
In the datacontext I have:
public ReadOnlyCollection<MetaDataMember> ColumnNames<TEntity>( )
{
return this.Mapping.MappingSource.GetModel(typeof(DataContext)).GetMetaType(typeof(TEntity)).DataMembers;
}
Fetching the table column-names that are properties inside the class:
MyDataContext db = GetDataContext();
var allColumnPropertyNames = db.ColumnNames<Animal>().Where(n => n.Member.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false).FirstOrDefault() != null).Select(n => n.Name);
Let's say (from the first sample, method update of a class MyClass):
public class MyClass {
public int iMyStatusProperty { get; set; }
public int iMyKey { get; set; }
public int UpdateStatusProperty(int iValue){
this.iMyStatusProperty = iValue;
return _Update( new[iMyStatusProperty ], iMyKey); // this should generate SQL: "UPDATE MyClass set iMyStatusProperty = {iMyStatusProperty} where iMyKey = {iMyKey}"
}
{iMyStatusProperty} and {iMyKey} are property values of a class instance.
So, the problem is how to get property name (reflection) from a property without using names of properties as strings (to avoid field name typos).

Categories