linq and object initialisation - linq

If I have something like:
var query = from children in _data.Children
where children.ChildId == childId
select new CustomModel.MyChild
{
ChildId = children.ChildId,
Name = children.ChildName
};
return query.FirstOrDefault();
Where I want the resultant object to be my custom model.
Can I handle the custom model instantiation in a different method, which could be reused if I had multiple linq queries that all generated a custom child model?
For example,
var query = from children in _data.Children
where children.ChildId == childId
select CreateMyCustomChild([param ??]);
return query.FirstOrDefault();
This may well be impossible, I don't know, but what would the method signature be like if it is possible?
I'm only thinking reuse for when multiple linq queries contain duplicate object initialisation code.
Thanks

It really depends on what version of LINQ you're using. If you're using LINQ to SQL, I don't believe you can call arbitrary methods in the query. The query translator wouldn't know what to do with the method call
If you're using LINQ to Objects, you're absolutely fine to do it, like this:
var query = from children in _data.Children
where children.ChildId == childId
select CreateMyCustomChild(children)
return query.FirstOrDefault();
// Elsewhere
public CustomModel.MyChild CreateMyCustomChild(OtherChild child)
{
return new CustomModel.MyChild
{
ChildId = child.ChildId,
Name = child.ChildName
};
}
(Side note: I'd call the range variable in the query "child" rather than "children" as at any one time it only represents a single child.)

If you wanted you could write "select 1" or in your case "CreateMyCustomChild(children)" since "children" is containing all your info. In your case you aren't adding a lot of info to "children", so why not "select children"?
In other words, just try it out. The return type of your value will determine over which type your LINQ enumerates.

Suppose you had a method that did the transform for you.
public static class Conversions
{
public static CustomModel.MyChild ToCustomModel(this DataModel.MyChild source)
{
return new CustomModel.MyChild()
{
ChildId = source.ChildId,
Name = source.ChildName
}
}
}
You can use such a method to do the conversion of a single item.
DataModel.MyChild myResult = getResult();
CustomModel.MyChild myConvertedResult = myResult.ToCustomModel()
Such a method can also be used in a Enumerable.Select method call.
IEnumerable<DataModel.MyChild> myQueriedResults = getResult();
IEnumerable<CustomModel.MyChild> myConvertedResults =
myQueryiedResults.Select(c => c.ToCustomModel());

While you can do with expressions, I don't think it is worth the hassle. Instead I suggest you define an extension method like:
IQueryable<CustomModel.MyChild> ToModel(this IQueryable<Child> childs)
{
return childs.Select( c=>
select new CustomModel.MyChild
{
ChildId = children.ChildId,
Name = children.ChildName
}
);
}
You can then call:
return _data.Children
.Where(c=>c.ChildId == childId)
.ToModel()
.FirstOrDefault();

Related

Is it Possible to use reflection on LINQ to Entities to query a dynamic table?

There are many tables in the database that are used as "lookup" tables. All the tables have the same structure, other than the ID column name.
I have found that I can use reflection to open a table and enumerate through the records. The method takes a string (tableName).
Uri serviceUri = new Uri("http://localhost/MyDataService/WcfDataService.svc");
var context = new MyEntities(serviceUri);
var eTable = typeof(MyEntities).GetProperty(tableName).GetValue(context, null) as IEnumerable<object>
foreach (object o in eTable)
...
This works fine, but I want to add a WHERE clause to the query. For example, where InactiveDate == null.
Can I do this? I have been unable to figure this one out.
How about this?
var eTable = (typeof(MyEntities).GetProperty(tableName).GetValue(context, null) as IEnumerable<object>).Where(obj => obj.GetType().GetProperty("InactiveDate").GetValue(obj) == null);
foreach (object o in eTable)
I would suggest to use generics and maybe an interface over reflection.
public function Xyz<TEntity>(Func<MyEntities, IDbSet<TEntity>> dbSetGetter, Expression<Func<TEntity, Boolean>> filter)
{
var serviceUri = new Uri("http://localhost/MyDataService/WcfDataService.svc");
using (var context = new MyEntities(serviceUri))
{
foreach (var entity in dbSetGetter(context).Where(filter))
{
DoSomethingWith(entity);
}
}
}
Usage would be like this
Xyz(context => context.Foo, foo => foo.Bar == 42);
assuming you have an entity Foo with an integer property Bar. The obvious difference to your code is that you have to know the entity type a compile time and I am not sure if you know it then.

linq: Using methods in select clause

I'm breaking my head with this and decided to share my problem with you
I want to create an anonymous select from several tables, some of them may contain more than one result. i want to concatenate these results into one string
i did something like this:
var resultTable = from item in dc.table
select new
{
id= item.id,
name= CreateString((from name in item.Ref_Items_Names
select name.Name).ToList()),
};
and the CreateString() is:
private string CreateString(List<string> list)
{
StringBuilder stringedData = new StringBuilder();
for (int i = 0; i < list.Count; i++)
{
stringedData.Append(list[i] + ", ");
}
return stringedData.ToString();
}
my intentions were to convert the "name" query to list and then sent it to CreateString() to convert it to one long concatenated string.
I tried using .Aggregate((current,next) => current + "," + next);
but when i try to convert my query to DataTable like below:
public DataTable ToDataTable(Object query)
{
DataTable dt = new DataTable();
IDbCommand cmd = dc.GetCommand(query as IQueryable);
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = (SqlCommand)cmd;
cmd.Connection.Open();
adapter.Fill(dt);
cmd.Connection.Close();
return dt;
}
I'm getting exception that "dc.GetCommand()" can't understand query with Aggregate method
later I tried to even use this simple query:
var resultTable = from itemin dc.table
select new
{
name = CreateString()
};
When CreateString() returns "success", nothing was inserted to "name"
why there is no way of using methods in select clause?
Thank you
Yotam
There is difference between LINQ to objects and LINQ to some-db-provider. Generally speaking, when using IQueryable, you can't use any methods, except the ones your provider understands.
What you can do is to retrieve the data from the database and then do the formatting using LINQ to objects:
var data = from item in dc.table
where /* some condition */
select item;
var result = from item in data.AsEnumerable()
select new
{
name = SomeFunction(item)
}
The AsEnumerable() extension method forces processing using LINQ to objects.
Forgive me if I've miss interpreted your question. It seems that what you are trying to do is abstract your select method for reuse. If this is the case, you may consider projection using a lambda expression. For example:
internal static class MyProjectors
{
internal static Expression<Func<Object1, ReturnObject>> StringDataProjector
{
get
{
return d => new Object1()
{
//assignment here
}
}
}
}
Now you can select your datasets as such:
dc.Table.Select(MyProjectors.StringDataProjector)
As for the concatenation logic, what about selecting to some base class with an IEnumerable<string> property and a read-only property to handle the concatenation of the string?

How to return the result set with columns with Linq

I have a function inside a class that will run a Linq to Entities query (or any type of Linq query actually), and it's gonna return 2 columns in the resultset. I would like to return an object to whoever is calling my function that will allow Intellisense to know what I have returned.
Let me explain. If I have a function like this:
public static IQueryable GetInfo(MyEntityModel oEntityModel)
{
var query =
(from t in oEntityModel.Table1
from u in t.Table2
where t.Status == true &&
u.Status == true
select new
{
t.Column1,
u.Column2
})
return query;
}
What can (should) I put instead of IQueryable so that whoever calls my GetInfo function, will get Intellisense from the resultset, and show that it has a Column1 and Column2?
var linqresult = ClsLinqTeste.GetInfo(oEntityModel);
if (linqresult.Column1 == 1)
{
foreach (var oItem in linqresult)
{
.. do stuff...
}
}
Tks
You cannot return an anonymous type from a function, they are strictly "inline" classes. When you return it, the foreach loop will only be able to interpret the result as an plain object. I guess you could use reflection to query the property names and values, however it seems much more straight forward to define a data transfer type to hold the results.
See this question, and this blog post.
So you could create a simple struct or class:
public class MyDataResult
{
public object Column1 { get; set; }
public object Column2 { get; set; }
}
Then modify your query in the function:
public static IQueryable<MyDataResult> GetInfo(MyEntityModel oEntityModel)
{
var query =
(from t in oEntityModel.Table1
from u in t.Table2
where t.Status == true &&
u.Status == true
select new MyDataResult
{
Column1 = t.Column1,
Column2 = u.Column2
})
return query;
}
Something like that should work. Note that I used "object" for the properties in MyDataResult. I don't know the types of the columns you are returning, you should use the actual types in order to get full intellisense.
You are returning a collection of anonymous types, they will be casted to objects, so when you try to iterate over them, altough they will be your objects (and they will contain your properties) at compile time they will be casted to objects:
foreach (var x in ClsLinqTeste.GetInfo(oEntityModel))
{
//x is an Object
}
You can read more about it here.
If you want to have intellisense, I suggest you create a custom class they will hold your properties and return not an anonymous type (using new {}) but object of your class (new MyClass(prop1, prop2)). You also need to change signature of your method, so it returns IQueryable<YourClass> and not just plain non-generic IQueryable.
As others have said, creating a new type to hold the two columns is usually the best option.
But if, for some reason, you don't want to do that and you are using .Net 4.0, you can use Tuple:
public static IQueryable<Tuple<Column1Type, Column2Type>>
GetInfo(MyEntityModel oEntityModel)
{
return from …
select Tuple.Create(t.Column1, u.Column2);
}
var linqresult = ClsLinqTeste.GetInfo(oEntityModel);
foreach (var oItem in linqresult)
Console.WriteLIne(oItem.Item1, oItem.Item2);
When you return your resultset AsQueryable, the app is already able to give you intellisense, however in your example, you must specify either .FirstOrDefault if you know your collection will only have a single row, or iterate over your collection to get the items from it, like so:
This is what you're doing:
var linqresult = ClsLinqTeste.GetInfo(oEntityModel);
if (linqresult.Column1 == 1)
{
..do stuff...
}
This is how you should do it:
var linqresult = ClsLinqTeste.GetInfo(oEntityModel);
foreach(var item in linqresult)
{
if (item.Column1 == 1)
{
..do stuff...
}
}
You must iterate over linqresult because when you query with link, it returns a result set, even if it just has one column. As with any collection, your data columns aren't available on the whole result set, only with individual items.
If you want to strongly typed enumerate a non-generic IEnumerable (IEnumerable.GetEnumerator() instead of IEnumerable<T>.GetEnumerator<T>()) you can use the Cast<>() extension, like so
var myquery = GetQueryable();
for (var item in myquery.Cast<MyDataType>())
{
// use item.Column1 directly and strongly typed with intellisense
}

How can I create an Expression within another Expression?

Forgive me if this has been asked already. I've only just started using LINQ. I have the following Expression:
public static Expression<Func<TblCustomer, CustomerSummary>> SelectToSummary()
{
return m => (new CustomerSummary()
{
ID = m.ID,
CustomerName = m.CustomerName,
LastSalesContact = // This is a Person entity, no idea how to create it
});
}
I want to be able to populate LastSalesContact, which is a Person entity.
The details that I wish to populate come from m.LatestPerson, so how can I map over the fields from m.LatestPerson to LastSalesContact. I want the mapping to be re-useable, i.e. I do not want to do this:
LastSalesContact = new Person()
{
// Etc
}
Can I use a static Expression, such as this:
public static Expression<Func<TblUser, User>> SelectToUser()
{
return x => (new User()
{
// Populate
});
}
UPDATE:
This is what I need to do:
return m => (new CustomerSummary()
{
ID = m.ID,
CustomerName = m.CustomerName,
LastSalesContact = new Person()
{
PersonId = m.LatestPerson.PersonId,
PersonName = m.LatestPerson.PersonName,
Company = new Company()
{
CompanyId = m.LatestPerson.Company.CompanyId,
etc
}
}
});
But I will be re-using the Person() creation in about 10-15 different classes, so I don't want exactly the same code duplicated X amount of times. I'd probably also want to do the same for Company.
Can't you just use automapper for that?
public static Expression<Func<TblCustomer, CustomerSummary>> SelectToSummary()
{
return m => Mapper.Map<TblCustomer, CustommerSummary>(m);
}
You'd have to do some bootstrapping, but then it's very reusable.
UPDATE:
I may not be getting something, but what it the purpose of this function? If you just want to map one or collection of Tbl object to other objects, why have the expression?
You could just have something like this:
var customers = _customerRepository.GetAll(); // returns IEnumerable<TblCustomer>
var summaries = Mapper.Map<IEnumerable<TblCustomer>, IEnumerable<CustomerSummary>>(customers);
Or is there something I missed?
I don't think you'll be able to use a lambda expression to do this... you'll need to build up the expression tree by hand using the factory methods in Expression. It's unlikely to be pleasant, to be honest.
My generally preferred way of working out how to build up expression trees is to start with a simple example of what you want to do written as a lambda expression, and then decompile it. That should show you how the expression tree is built - although the C# compiler gets to use the metadata associated with properties more easily than we can (we have to use Type.GetProperty).
This is always assuming I've understood you correctly... it's quite possible that I haven't.
How about this:
public static Person CreatePerson(TblPerson data)
{
// ...
}
public static Expression<Func<TblPerson, Person>> CreatePersonExpression()
{
return d => CreatePerson(d);
}
return m => (new CustomerSummary()
{
ID = m.ID,
CustomerName = m.CustomerName,
LastSalesContact = CreatePerson(m.LatestPerson)
});

How to access data into IQueryable?

I have IQueryable object and I need to take the data inside the IQueryable to put it into Textboxs controls. Is this possible?
I try something like:
public void setdata (IQueryable mydata)
{
textbox1.text = mydata.????
}
Update:
I'm doing this:
public IQueryable getData(String tableName, Hashtable myparams)
{
decimal id = 0;
if (myparams.ContainsKey("id") == true)
id = (decimal)myparams["id"];
Type myType= Type.GetType("ORM_Linq." + tableName + ", ORM_Linq");
return this.GetTable(tableName , "select * from Articu where id_tipo_p = '" + id + "'");
}
public IQueryable<T> GetTable<T>(System.Linq.Expressions.Expression<Func<T, bool>> predicate) where T : class
{
return _datacontext.GetTable<T>().Where(predicate);
}
This returns a {System.Data.Linq.SqlClient.SqlProvider+OneTimeEnumerable1[ORM_Linq.Articu]}`
I don't see any method like you tell me. I see Cast<>, Expression, ToString...
EDIT: Updated based on additional info from your other posts...
Your getData method is returning IQueryable instead of a strongly typed result, which is why you end up casting it. Try changing it to:
public IQueryable<ORM_Linq.Articu> getData(...)
Are you trying to query for "Articu" from different tables?
With the above change in place, your code can be rewritten as follows:
ORM_Linq.Articu result = mydata.SingleOrDefault();
if (result != null)
{
TextBoxCode.Text = result.id.ToString();
TextBoxName.Text = result.descrip;
}
If you have a single result use SingleOrDefault which will return a default value if no results are returned:
var result = mydata.SingleOrDefault();
if (result != null)
{
textbox1.text = result.ProductName; // use the column name
}
else
{
// do something
}
If you have multiple results then loop over them:
foreach (var item in mydata)
{
string name = item.ProductName;
int id = item.ProductId;
// etc..
}
First, you should be using a strongly-typed version of IQueryable. Say that your objects are of type MyObject and that MyObject has a property called Name of type string. Then, first change the parameter mydata to be of type IQueryable<MyObject>:
public void setdata (IQueryable<MyObject> mydata)
Then we can write a body like so to actually get some data out of. Let's say that we just want the first result from the query:
public void setdata (IQueryable<MyObject> mydata) {
MyObject first = mydata.FirstOrDefault();
if(first != null) {
textbox1.Text = first.Name;
}
}
Or, if you want to concatenate all the names:
public void setdata(IQueryable<MyObject> mydata) {
string text = String.Join(", ", mydata.Select(x => x.Name).ToArray());
textbo1.Text = text;
}
Well, as the name suggests, an object implementing IQueryable is... Queryable! You'll need to write a linq query to get at the internal details of your IQueryable object. In your linq query you'll be able to pull out its data and assign bits of it where ever you'd like - like your text box.
Here's a great starting place for learning Linq.
I think you find the same mental struggle when coming from FoxPro and from DataSet. Really nice, powerful string-based capabilities(sql for query, access to tables and columns name) in these worlds are not available, but replaced with a compiled, strongly-typed set of capabilities.
This is very nice if you are statically defining the UI for search and results display against a data source known at compile time. Not so nice if you are trying to build a system which attaches to existing data sources known only at runtime and defined by configuration data.
If you expect only one value just call FirstOrDefault() method.
public void setdata (IQueryable mydata)
{
textbox1.text = mydata.FirstOrDefault().PropertyName;
}

Resources