Access a property of a DbSet by name - c#

I have a DbSet<T>
I want to access one of the properties on it by name.
Is this possible?
I'm basically making a generic function, so that for any DbSet I have, I can specify a column and have it list the values of that column.
You can get the DbSet itself from the context by doing context.Set(T) but I am not sure about the fields.

I did something similar a while back using reflection.
T item = context.Set(T).First();
string propName = "MyProperty";
object value = item.GetType().GetProperty(propName).GetValue(item, null);
Of course note that you'll either need to cast the values to a specific type manually, or use ToString, which should work quite well on all basic types.
This assumes you already have the data from the server, and now need to process it.
EDIT:
If you want to create a query, then I found this!
Apparently, what you're looking for is available as part of Entity Framework these days.
An extension method is available which allows you to use .Select("propertyName") which returns IQueriable. Remember to add System.Linq.Dynamic to your using section.
You can then create select queries by specifying the name of the parameter.
List<object> data = (db.Set<SetType>()
.Where("propertyName == #0 && someOtherProperty == #1", propertyValue, someOtherPropertyValue)
.Select("propertyName") as IEnumerable<object>).ToList();

Check out this article on Dynamic LINQ.
Using the provided code, I was able to write a LINQ to Entities query like this:
var query = context.Set(Type.GetType("Person")).Select("Name");

Related

Using Dynamic LINQ Where in a one-to-many Entity Framework relationship

I am using EF 6 and .net 4.5.
I have a "Documents" table with the PK "DocumentId" (Int) and another column, "Title". I have another table called "DocumentFilters" with the columns "DocumentId" (Int) and "DocGUID" (Guid). There is a FK on "Doc Id" between the two tables. It is a one to many Id's relationship between the Documents and DocumentFilters tables. The Model looks like this:
I'm trying to write a Dynamic LINQ query using the DynamicLibrary.cs library from the NuGet package.
With regular Linq, the query looks something like this in c#:
DocDBContext db = new DocDBContext();
var documents = db.Documents.Include(d => d.DocumentFilters);
Guid gTest = Guid.Parse("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
documents = documents.Where(d => d.DocumentFilters.Select(f => f.DocGUID).Contains(gTest));
This works perfectly and retrieves the documents delimited by the GUID filter that I'm interested in. However, I need to place this within a filter string dynamically, where it may contain other filters. Is it possible to do that? I've tried code like:
documents = documents.Where("DocumentFilters.Select(DocGUID).Contains(#0)", gTest);
Error: No applicable aggregate method 'Select' exists
or
documents = documents.Where("DocumentFilters.DocGUID=#0", gTest);
Error: GUID property does not exist on DocumentFilters.
(Which makes sense because DocumentFilters is a collection).
I also thought it might be worth to give this a shot:
documents = documents.Where("DocumentFilters.AsQueryable().Select(DocGUID).Contains(#0)", gTest);
But again, it doesn't look like the library supports AsQueryable and throws
Error: No applicable aggregate method 'AsQueryable' exists.
Is it possible to write such a restriction on a property of an enumerable object within the Documents table?
So one solution was to modify Dynamic.cs by adding "Select" to the IEnumerableSignatures interface in the ExpressionParser class. And additionally add it to the ParseAggregate() method:
if (signature.Name == "Min" || signature.Name == "Max")
{
typeArgs = new Type[] { elementType, args[0].Type };
}
else if (signature.Name == "Select")
{
typeArgs = new Type[] { elementType, Expression.Lambda(args[0], innerIt).Body.Type };
}
However, after this I still needed to overload the Contains() method to work with GUIDs. I didn't bother to do that. Instead I realized I can simply use the already supported .Any() instead of .Select()!
documents = documents.Where("DocumentFilters.Any(DocGUID.Equals(#0))", gTest); //!!!
I also had to change the column name from "GUID" to "DocGUID" because when writing GUID.Equals() in the restriction string, the parser mistook it for the GUID type and not the column name, and threw the error that Equals() is not a valid extension for the type GUID.
Sorry for wasting everyone's time!
Try to use Dynamic Linq, it can be used against strings.

LINQPad LINQ To SQL Updates with Anonymous Types

Say I have the following query in LINQPad targeting a SQL DB (using C# Statement(s) mode):
var query = (from te in Time_Entries
select new { te.Period, te.Company_Name }).FirstOrDefault();
If I wanted to update the Period value on the selected record, I would think that I could do something like:
query.Period = 5;
SubmitChanges();
But unfortunately, I get an error on the query.Period assignment line:
Property or indexer 'AnonymousType#1.Period' cannot be assigned to -- it is read only
Is it possible to perform an update this way or in a similar way?
No, you can't. Anonymous types can't have properties that can be modified.
From the documentation:
Anonymous types provide a convenient way to encapsulate a set of
read-only properties into a single object without having to explicitly
define a type first.
It doesn't really make sense anyway. Anonymous types are sometimes very useful, but not when you need to use Linq2Sql entity tracking and updating...
Well, the answer is already in the
select new {}
Even if it would not be an anonymous type, all it could be is an insert....
The rest is answered by walther in his answer.

Using Dynamic LINQ (or Generics) to query/filter Azure tables

So here's my dilemma. I'm trying to utilize Dynamic LINQ to parse a search filter for retrieving a set of records from an Azure table. Currently, I'm able to get all records by using a GenericEntity object defined as below:
public class GenericEntity
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
Dictionary<string, object> properties = new Dictionary<string, object>();
/* "Property" property and indexer property omitted here */
}
I'm able to get this completely populated by utilizing the ReadingEntity event of the TableServiceContext object (called OnReadingGenericEvent). The following code is what actually pulls all the records and hopefully filter (once I get it working).
public IEnumerable<T> GetTableRecords(string tableName, int numRecords, string filter)
{
ServiceContext.IgnoreMissingProperties = true;
ServiceContext.ReadingEntity -= LogType.GenericEntity.OnReadingGenericEntity;
ServiceContext.ReadingEntity += LogType.GenericEntity.OnReadingGenericEntity;
var result = ServiceContext.CreateQuery<GenericEntity>(tableName).Select(c => c);
if (!string.IsNullOrEmpty(filter))
{
result = result.Where(filter);
}
var query = result.Take(numRecords).AsTableServiceQuery<GenericEntity>();
IEnumerable<GenericEntity> res = query.Execute().ToList();
return res;
}
I have TableServiceEntity derived types for all the tables that I have defined, so I can get all properties/types using Reflection. The problem with using the GenericEntity class in the Dynamic LINQ Query for filtering is that the GenericEntity object does NOT have any of the properties that I'm trying to filter by, as they're really just dictionary entries (dynamic query errors out). I can parse out the filter for all the property names of that particular type and wrap
"Property[" + propName + "]"
around each property (found by using a type resolver function and reflection). However, that seems a little... overkill. I'm trying to find a more elegant solution, but since I actually have to provide a type in ServiceContext.CreateQuery<>, it makes it somewhat difficult.
So I guess my ultimate question is this: How can I use dynamic classes or generic types with this construct to be able to utilize dynamic queries for filtering? That way I can just take in the filter from a textbox (such as "item_ID > 1023000") and just have the TableServiceEntity types dynamically generated.
There ARE other ways around this that I can utilize, but I figured since I started using Dynamic LINQ, might as well try Dynamic Classes as well.
Edit: So I've got the dynamic class being generated by the initial select using some reflection, but I'm hitting a roadblock in mapping the types of GenericEntity.Properties into the various associated table record classes (TableServiceEntity derived classes) and their property types. The primary issue is still that I have to initially use a specific datatype to even create the query, so I'm using the GenericEntity type which only contains KV pairs. This is ultimately preventing me from filtering, as I'm not able to do comparison operators (>, <, =, etc.) with object types.
Here's the code I have now to do the mapping into the dynamic class:
var properties = newType./* omitted */.GetProperties(
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public);
string newSelect = "new(" + properties.Aggregate("", (seed, reflected) => seed += string.Format(", Properties[\"{0}\"] as {0}", reflected.Name)).Substring(2) + ")";
var result = ServiceContext.CreateQuery<GenericEntity>(tableName).Select(newSelect);
Maybe I should just modify the properties.Aggregate method to prefix the "Properties[...]" section with the reflected.PropertyType? So the new select string will be made like:
string newSelect = "new(" + properties.Aggregate("", (seed, reflected) => seed += string.Format(", ({1})Properties[\"{0}\"] as {0}", reflected.Name, reflected.PropertyType)).Substring(2) + ")";
Edit 2: So now I've hit quite the roadblock. I can generate the anonymous types for all tables to pull all values I need, but LINQ craps out on my no matter what I do for the filter. I've stated the reason above (no comparison operators on objects), but the issue I've been battling with now is trying to specify a type parameter to the Dynamic LINQ extension method to accept the schema of the new object type. Not much luck there, either... I'll keep you all posted.
I've created a simple System.Refection.Emit based solution to create the class you need at runtime.
http://blog.kloud.com.au/2012/09/30/a-better-dynamic-tableserviceentity/
I have run into exactly the same problem (with almost the same code :-)). I have a suspicion that the ADO.NET classes underneath somehow do not cooperate with dynamic types but haven't found exactly where yet.
So I've found a way to do this, but it's not very pretty...
Since I can't really do what I want within the framework itself, I utilized a concept used within the AzureTableQuery project. I pretty much just have a large C# code string that gets compiled on the fly with the exact object I need. If you look at the code of the AzureTableQuery project, you'll see that a separate library is compiled on the fly for whatever table we have, that goes through and builds all the properties and stuff we need as we query the table. Not the most elegant or lightweight solution, but it works, nevertheless.
Seriously wish there was a better way to do this, but unfortunately it's not as easy as I had hoped. Hopefully someone will be able to learn from this experience and possibly find a better solution, but I have what I need already so I'm done working on it (for now).

LINQ to SQL Custom Property query on where clause

I am using LINQ to SQL classes, and have extended one (with a partial class) and added an extra property. I want to query on this property, so:
(from p in db.TableName where p.CustomProperty=="value" select p)
However this doesn't work. I get an error: The member 'TableName.CustomProperty' has no supported translation to SQL. At the moment I have no code in the 'set' section of my Custom Property as I'm unsure how.
So basically, Custom Property which can be queried on with LINQ to SQL, how?
As a follow up: this CustomProperty is NOT a column in the table. It is a separate value, but I need to both fetch it (easy enough) but also query on it!
As you can understand, there can't be any magic, so essentially there will be two queries: first one is a SQL query with database criteria and on its result there should be applied your custom criteria as a second query.
So the workaround you could use is to split two parts explicitly like this:
var dbFetch = (from p in db.TableName where p.RealProperty ==" value" select p).ToArray();
var result = from p in dbFetch where p.CustomProperty == "value" select p;
But of course you'll run into several limitations. For example if you fetching results page-by-page, the second criterion will break paging since it performs additional filtering.
HTH
It's called LINQ to SQL. Just to avoid misunderstandings.
About your problem: have you added that property using the designer? And have you re-created the database after that?
If you did it by hand, make sure you have a private storage field (like _CustomProperty), and your property (CustomProperty) is marked with the ColumnAttribute, e.g.
private string _CustomProperty;
[Column(Storage="_CustomProperty", CanBeNull=true)]
public string CustomProperty
{
get { return _CustomProperty; }
}
Hope this helps.
Aren't you missing an equality sign there? In C#, equality is expressed with double equal signs, as in "a == b", while single equal sign signifies assignment, as in "obj.SomeProp = 5;"
I have implemented a system where you can query manually added properties that represent enumerated wrappers around integer properties from database columns. So I know it's possible, and it looks like you might be wanting to do something similar. The way I did it was not easy, though, and unless you are building a framework that you want to use properly for many cases, you might be better off using the solution suggested by archimed7592. I don't have the code handy at the moment so I can't provide all the details, but briefly my solution works like this. I created a custom LINQ provider, replacing the LINQ-to-SQL provider. I did this by implementing a custom IQueryable interface that returned my LINQ provider instead of that provided by LINQ-to-SQL. Then, in the functions that take expression objects, I pre-processed the expression before returning the result. I replaced all comparisons between enum-type properties and enum values with comparisons between integer properties and integer values, then passed that expression to the normal LINQ-to-SQL implementation in order to return the result. Since expressions are read-only, I had to make a (recursive) function that re-built the entire expression with the customized parts replaced.

Can I start with a string and instantiate an object of that string?

I am currently working with LINQ and C#.
I have a DropDownList of the tables in my LINQ to SQL model.
I want the user to be able to select a LINQ table name from the DropDown. In code I want to create an instance of that LINQ class and then run a Select or it or anything else I want.
How would I accomplish creating the object based on what object name in string the user chose? Am I thinking incorrectly from the start?
You want Type.GetType(string) and Activator.CreateInstance(Type).
Note that Type.GetType(string) will only look in the currently executing assembly and mscorlib unless you specify the full type name including assembly. In either case, you need to specify the type name including namespace.
Another alternative is to use Assembly.GetType(string) to get the type directly from the string before calling Activator.CreateInstance.
(There are actually lots of alternatives here. If none of these help you, please post more info and I'm sure we can work out a way to go.)
Since you tagged the post with ASP.NET, I am assuming that the list is on the client side. If that is the case, you should be very careful about trusting that data, and I would not recommend creating types directly from user input. You could use the data as input to a factory that could then return the proper instance (and handle any illegal input as you see fit).
Elaborating on Brian Rasmussen's warning: The types should be restricted and require conscious design. Preferable the "user-instantiable" types should be marked with a specific custom attribute that can be verified with reflection.
With LINQ-to-SQL, there are specific ways of doing this from the data-context; basically, db.GetTable. This returns an ITable, but it is a little tricky to work with an untyped ITable. You can enumerate it, at least...
To get the ITable, you normally need the Type, which you can get with (for example) Assembly.GetType:
using (var ctx = new MyDataContext()) {
string name = "Customer"; // type name
Type ctxType = ctx.GetType();
Type type = ctxType.Assembly.GetType(
ctxType.Namespace + "." + name);
ITable table = ctx.GetTable(type);
foreach(var row in table) {
Console.WriteLine(row); // works best if ToString overridden...
}
}
Of course, once you have the Type, you use use Activator to create new entity instances:
object newObj = Activator.CreateInstance(type);
// TODO: set properties (with reflection?)
table.InsertOnSubmit(newObj);
but if you want to use the property-name, that can work too:
using (var ctx = new MyDataContext()) {
string name = "Customers"; // property name
ITable table = (ITable) ctx.GetType()
.GetProperty(name).GetValue(ctx, null);
foreach (var row in table) {
Console.WriteLine(row); // works best if ToString overridden...
}
}
Running filters (Where) etc is tricky with untyped data, as building the Expression would be tortuous. I'd probably start switching to a typed model at that point...
To follow on Marc Gravell's answer.
Doing as he suggested, I noticed a Cast<TResult> extension method (defined in System.Linq).
Unfortunately you can't seem to be able to do use the type instance to cast:
Type dcType = dc.GetType();
Type type = dcType.Assembly.GetType(String.Format("{0}.{1}", dcType.Namespace, name));
var row = dc.GetTable(type).Cast<type>().SingleOrDefault(i => i.ID == 123);

Categories