Formatting Select Statement Using Dynamic Linq - c#

I've been looking into this for quite some time now and cannot figure out a resolution. I Originally Tried formatting A dynamic linq Statement as you can see here in this post
I declared a class:
public class DynamicHelper
{
public string FormattedLink(string DisplayText, string ID)
{
return "" + DisplayText + "";
}
public string FormattedLink(string DisplayText, int ID)
{
return "" + DisplayText + "";
}
}
After I inserted a new type in DynamicLinq into the predefinedTypes
,typeof(DynamicHelper) //around line 635
I have a program which is attempting to Invoke the FormattedLink inside of a dynamic linq select:
using (var Model = new MK3Entities())
{
DynamicHelper Dh = new DynamicHelper();
var TOrigin = (Model.Titles.Where("ID > 19632")
.Select("new(ID, #0.FormattedLink(ExtTitleID, ID) as ExtTitleID )", Dh) as System.Collections.IEnumerable)
.Cast<dynamic>().Take(10).ToList();
Console.ReadKey();
}
When I execute this program I get a runtime exception "LINQ to Entities does not recognize the method 'System.String FormattedLink(System.String, Int32)' method, and this method cannot be translated into a store expression."
Any Ideas on how to fix this... I just need simple formatting from Dynamic Select.

The error message is pretty self explanatory. The database doesn't know how to translate that method into SQL. You need to fetch the information that the method needs in your database query and then call that function on the results, rather than in the query.

I'm not sure why you need it to be dynamic, it seems the solution you present is very overly complicated. I would write it as:
using (var Model = new MK3Entities())
{
DynamicHelper Dh = new DynamicHelper();
var TOrigin = Model.Titles
.Where("ID > 19632")
.Select(t => new { ID = t.ID, ExtTitleID = t.ExtTitleId })
.Take(10)
.ToList() // Execute SQL Statement
.Select(t => new {ID = t.ID, Link = nh.FormattedLink(ExtTitleID, ID)})
.ToList();
Console.ReadKey();
}
I'm returning an List<anonymous'1> object instead of a dynamic object (because I've never had the need for dynamic objects) so you can adjust it accordingly.

I just solved similiar problem few hours back.
YOu need ToList() that works with Dynamic linq. Check out this thread: Can't find property or field on a dynamic object
Just copy paste those to your project, and later:
var TOrigin = (Model.Titles.Where("ID > 19632")
.ToAnonymousList()
.Select("new(ID, #0.FormattedLink(ExtTitleID, ID) as
ExtTitleID )", Dh) as System.Collections.IEnumerable);

Related

Dynamic query using LINQ to SQL

I need to figure out if it is possible to dynamically build a query with LINQ, dynamically selecting the table in which to perform the query.
This is an example of what I would do:
//Not working,just for example
public List<dynamic> _getGenericList(String tableName)
{
var l = from a in db.//I need to use here tableName
select a;
return l.ToList<dynamic>();
}
Is there a way to make this possible?
If the query is this simple you can dynamically create a standard sql statement and execute it, this is the most simplest way without using processor heavy reflection and complex code?
var query = "SELECT * FROM " + tableName;
var res = context.ExecuteQuery<dynamic>(query).ToList();
I've found a way to do it, but I'm not sure if I'd use this code. If you have a DataContext that contains two tables:
PrimaryTable
ID,
FirstValue,
SecondValue
SecondaryTable
ID,
FirstSecondaryValue
You could use the following DataHelper class:
class DataHelper
{
public MyDatabaseDataContext db = new MyDatabaseDataContext();
List<dynamic> GetDynamicList<T>() where T : class
{
System.Data.Linq.Table<T> table = db.GetTable<T>();
var result = from a in table select a;
return result.ToList<dynamic>();
}
public List<dynamic> GetWhatIWant(string tableName)
{
Type myClass = Type.GetType("DynamicLinqToSql." + tableName);
MethodInfo method = typeof(DataHelper).GetMethod("GetDynamicList", BindingFlags.NonPublic | BindingFlags.Instance);
method = method.MakeGenericMethod(myClass);
return (List<dynamic>)method.Invoke(this, null);
}
}
Then you can create an instance of your DataHelper and call the GetWhatIWant method, passing in the table name.
var dataHelper = new DataHelper();
List<dynamic> myFirstList = dataHelper.GetWhatIWant("PrimaryTable");
for (int i = 0; i < 5 && i < myFirstList.Count; i++)
{
System.Console.WriteLine(String.Format("{0} - {1}", myFirstList[i].FirstValue.ToString(), myFirstList[i].SecondValue.ToString()));
}
List<dynamic> mySecondList = dataHelper.GetWhatIWant("SecondaryTable");
for (int i = 0; i < 5 && i < mySecondList.Count; i++)
{
System.Console.WriteLine(mySecondList[i].FirstSecondaryValue.ToString());
}
System.Console.ReadKey();
I know this is old, but if you are here looking for answers like I was, then maybe this will help. I'm using a .NET ObjectContext directly instead of a DataContext data source. If you are using the DataContext version then you can simply (I hope) use queryResults = myGlobalContext.ExecuteQuery<dbGenericData>(query).ToList(); instead and I'm pretty sure it will work the same way.
Your tables will be a lot easier to work with if you have standards in naming and design like
the ID field for the table is always X type (INT, GUID, etc)
the ID field is always named tableNameID, the 'table name' with ID tagged on.
etc,
This will allow you to easily build the ID field by simply appending the 'ID' string onto the table name and will allow you to use a reliable CAST, if needed.
Speaking of CAST you will notice one in the query string. You will need to modify the use of the SQL string using CAST, changing field lengths like my nvarChar(50) example, etc, to overcome getting various TYPES of data from your database.
Final note: In the query string you will see I use the 'AS' key word to cast the DB field to a new name. I cast the 'tableIDField' into the name 'id' and I cast the 'requestedField' into the name 'dbData'. This allows the system to match up the renamed fields from the DB into the STRUCT object container we dump the data into. This allows you to construct generic containers to hold the data returned without having to worry about matching up with the DB field names.
I'm not a guru at this stuff, but I hope this helps somebody out.
private void testMethod(string requestedField, string tableName)
{
var tableIDField = tableName + "ID";
var query = "select " + tableIDField + " as id, CAST(" + requestedField + "as nvarchar(50)) as dbData from " + tableName;
List<dbGenericData> queryResults = null;
try
{
queryResults = myGlobalContext.ExecuteStoreQuery<dbGenericData>(query).ToList();
}
catch (Exception ex)
{
//Simply ignore any exceptions.
//These will need examined to determine best solution to unexpected results.
}
}
private struct dbGenericData
{
public dbGenericData(int id, string dbData)
{
this = new dbGenericData();
ID = id;
DBData = dbData;
}
public int ID { get; set; }
public string DBData { get; set; }
}
you can Generic Method and use db.Set<T> that return a DbSet Based on T
var esql = "select t from TypeName as t"
var q = db.CreateQuery(esql);
Use entity sql for linq to sql, http://esql.codeplex.com

How do I create a list object that contains a list of another object type using from within a query using linq and c#

I am trying to return a list of an object that contains another object list as a databmember using linq. I've tried the examples shown but I keep getting ad different error with each attempt. One of which is as follows: LINQ to Entities does not recognize the method 'System.Collections.Generic.List1[SunGard.Tools.Notifications.LinkVariable] ToList[LinkVariable](System.Collections.Generic.IEnumerable1[SunGard.Tools.Notifications.LinkVariable])' method, and this method cannot be translated into a store expression.
I have an object (AlertMessageReturn) that contains some string datamembers as well as a list aof another object (List). I have a class that defines the LinkVarible and a table that contains the values. My query looks like this:
AlertMessagesQuery = from alertMessage in this.context.AlertMessages
where alertMessage.UserId=UserId
select new AlertMessageReturn()
{ PAM_ShortMessage = alertMessage.PAM_ShortMessage,
PAM_LongMessage = alertMessage.PAM_LongMessage,
PAM_LongMessageRemote = alertMessage.PAM_LongMessageRemote,
LinkVariables = (from linkVariable in this.context.AlertMessageLinks
from user in this.context.AlertMessageUsers
where user.PAMU_PAM_ID == linkVariable.PAML_PAM_ID && user.PAMU_UserId == UserId
select new LinkVariable()
{
Name = linkVariable.PAML_SessionVariableName,
Value = linkVariable.PAML_SessionVariableValue
})
};
The error is related to the type returned for linkvariables.
Please help.
I changed the code as follows:
LinkDataQuery = from linkData in this.context.AlertMessageLinks
from user1 in this.context.AlertMessageUsers
where user1.PAMU_PAM_ID == linkData.PAML_PAM_ID && user1.PAMU_UserId == UserId
select new LinkData
{
Name = linkData.PAML_SessionVariableName,
Value = linkData.PAML_SessionVariableValue
};
var links = LinkDataQuery.ToList();
AlertMessagesQuery = from alertMessage in this.context.AlertMessages
where alertMessage.UserId=UserId
select new AlertMessageReturn()
{ PAM_ShortMessage = alertMessage.PAM_ShortMessage,
PAM_LongMessage = alertMessage.PAM_LongMessage,
PAM_LongMessageRemote = alertMessage.PAM_LongMessageRemote,
LinkVariables = links
};
var AlertMessages = AlertMessagesQuery.ToList(); // this is where the error point to
if (AlertMessages.Any())
{
return AlertMessages;
}
The error I now get is:System.NotSupportedException: Unable to create a constant value of type 'SunGard.Tools.Notifications.LinkData'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
The LINQ to SQL engine cannot turn your sub-query to generate the LinkVariables into SQL. More importantly, SQL cannot return nested data sets like that.
Any time you get a message of type 'cannot be translated into a store expression' it is an indicator that you are doing something with your linq that is attempting to be translated into other statements (usually SQL). For example, if you say
....select new MyObject
{
Id = Guid.Parse( passedIdentity ),
....
}
while this is a totally valid C# statement you will get an error that Guid.Parse cannot be handled by linq. If it is possible to move the variables into external variables that are used inside the query then it would work. So you would do...
string name = linkVariable.PAML_SessionVariableName;
string nValue = ....
....
select New LinkVariable
{
Name=name,
Value=nValue
};
Also ... you do not need the closing parens on the Select New statement.
While LINQ to SQL can bring back object heirarchies, it can't project into types that aren't part of the model. Instead of projecting into the AlertMessageReturn type, try projecting into an anonymous type in the IQueryable portion of the code. Once you're done structuring your database query, force the results to come back (using AsEnumerable) and then project that into your AlertMessageReturn type. It's more overhead, but does work. Alternatively, you can use something like AutoMapper to translate your anonymous type into the result types.
AlertMessagesQuery =
from alertMessage in this.context.AlertMessages
where alertMessage.UserId=UserId
select new
{
alertMessage.PAM_ShortMessage,
alertMessage.PAM_LongMessage,
alertMessage.PAM_LongMessageRemote,
LinkVariables = from linkVariable in this.context.AlertMessageLinks
from user in this.context.AlertMessageUsers
where user.PAMU_PAM_ID == linkVariable.PAML_PAM_ID && user.PAMU_UserId == UserId
select new
{
Name = linkVariable.PAML_SessionVariableName,
Value = linkVariable.PAML_SessionVariableValue
})
};
var alertMessageResults =
from message in AlertMessagesQuery.AsEnumerable()
select new AlertMessageResult
{
PAM_ShortMessage = mesage.PAM_ShortMessage,
PAM_LongMessage = message.PAM_LongMessage,
PAM_LongMessageRemote = message.PAM_LongMessageRemote,
LinkVariables = (from variable in message.LinkVariables
select new LinkVariable { Name=variable.Name, Value = variable.Value})
.ToList()
};
return alertMessageResults.ToList();

Linq To SQL Method with .Contains fails

I have the below Linq To SQL Method. When I step through the code spcCodeIDs contains the seven entries I am expecting. However I get a run-time exception of
Method 'Boolean Contains(System.String)' has no supported translation to SQL.
What am I missing?
public static DataTable GetSPCCodeList()
{
using (var context = ProviderDataContext.Create())
{
IQueryable<tblProviderAdminSPCCode> tSPCCode = context.GetTable<tblProviderAdminSPCCode>();
IList<string> spcCodeIDs = BLLCmo.FCApprovedSPCsForGreenSheet();
return (tSPCCode
.Where(spcCode => spcCode.Inactive == null && spcCodeIDs.Contains(spcCode.SPCCodeID))
.OrderBy(spcCode => spcCode.SPCCodeID)
.Select(spcCode => new { spcCode.SPCCodeID, spcCode.SPCDescription, spcCode.SPCCategoryID }))
.CopyLinqToDataTable();
}
}
LINQ to SQL can only support Contains translations form a concrete list and not the IList interface.. try changing your line from
IList<string> spcCodeIDs = BLLCmo.FCApprovedSPCsForGreenSheet();
to
List<string> spcCodeIDs = BLLCmo.FCApprovedSPCsForGreenSheet().ToList();
You need to pass a string as a parameter to Contains. So trying passing spcCode.SPCCodeID.ToString()

Linq-to-sql logic pain

I've been trying to get the following method cleaned up using more sensible and lean syntax, but I'm striking serious headaches when it comes to aggregate clauses and filtering using L2S. Particularly, I feel I should be able to use a .Contains() method to filter out objects whose tags fit the string parameter passed in the method, but it hasn't worked.
public TagListViewModel GetTagModel(string Name)
{
var model = new TagListViewModel();
var repo = new SimpleRepository("Wishlist");
var ideas = repo.All<Idea>();
List<Idea> ideaList = new List<Idea>();
foreach (Idea i in ideas)
{
var query = from tag in repo.All<Tag>()
join ideatag in repo.All<IdeaTag>()
on tag.ID equals ideatag.TagId
where ideatag.IdeaId == i.ID
select tag;
i.Tags = query.ToList<Tag>();
ideaList.Add(i);
}
foreach (Idea i in ideaList)
{
var query = from vote in repo.All<IdeaVotes>()
where vote.IdeaId == i.ID
select vote;
i.Votes = query.ToList<IdeaVotes>();
}
// Here begins the problem area. I should be able to get a tag from the repo
// whose name matches the "Name" parameter and then call a .Contains() method to
// filter this list, shouldn't I?
List<Idea> filteredTagList = new List<Idea>();
foreach (Idea item in ideaList){
foreach(Tag t in item.Tags)
{
if (t.Name == Name)
filteredTagList.Add(item);
}
}
model.Ideas = filteredTagList;
return model;
}
It's ugly. I know it's ugly but after over 2 hours of playing with several preferred variations I still can't get it to filter the way it's supposed to. Where am I going wrong?
This should be equivalent assuming there are no duplicate tags on a single Idea.
model.Ideas = ideaList.Where(
idea => idea.Tags.Any(
tag => tag.Name == Name)).ToList();

LINQ Guid toString()

Hi this seems like it should work,
from something in collectionofsomestuff
select new SelectListItem(){Text = something.Name, Value = something.SomeGuid.ToString(), Selected = false};
When I try to do this it doesn't work give me error
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
Is there a workaround?
Not all CLR methods can be used with Linq-to-Entities. ToString() seems to be one of them.
Take a look at CLR Method to Canonical Function Mapping.
Maybe try setting the GUID to a string variable explicitly, outside of Linq.
string myGuid = SomeGuid.ToString();
from something in collectionofsomestuff
select new SelectListItem(){Text = Name, Value = myGuid, Selected = false};
You can get the records in the db,and then turn them to a list or a array use ToList() or ToArray().Then use the object.
For example(it is LINQ to Entities ):
var list = collectionofsomestuff.select(c => c).ToList();
from something in list
select new SelectListItem(){Text = something.Name, Value = something.SomeGuid.ToString(), Selected = false};
I don't speak Linq query expressions too well, but the following should do the trick:
collectionofsomestuff //here it's LinqToEntities
.Select(something=>new{something.Name,something.SomeGuid})
.ToArray() //From here on it's LinqToObjects
.Select(s=>new SelectListItem()
{
Text = s.Name,
Value = s.SomeGuid.ToString(),
Selected = false
})
I ended up doing a foreach like so
List<SelectListItem> list = new List<SelectListItem>();
foreach (SomeThing something in collectionofsomestuff)
{
list.Add(new SelectListItem(){Text = something.Name,Selected = false,Value = something.SomeGuid.ToString()});
}
this is the only way I could get it to work..it wasn't what i was hoping to do tough..
Create a constructor for SelectListItem that accepts your Value as a Guid and ToString it there. Now call your query like so:
from something in collectionofsomestuff select new SelectListItem(something.Name, something.SomeGuid, false);
I had the same problem, and I ended up changing my object's definition to get around the problem. It's a complete hack, but it allows me to populate the data straight from the query:
[DataContract]
public class DeviceInfo
{
public Guid DeviceGuid
{
set
{
DeviceID = value.ToString();
}
}
[DataMember]
public string DeviceID { get; set; }
}
And the query works as designed because it has something else doing the conversion for it:
devices.AddRange(from d in ae.UserDevices
select new DeviceInfo
{
DeviceGuid = d.DeviceID
}
It makes the object a little messier, but makes dealing with the Guid in the query so much easier.

Categories

Resources