I have a SQL query that looks like
SELECT CONVERT(BIGINT, MY_TABLE_FIELD)
FROM SOME_TABLE
WHERE [CONDITIONS]
I need to cast the string MY_TABLE_FIELD to a long value field in Linq.
I've tried with long.tryParse, (long)MY_TABLE_FIELD, and Convert.ToInt64(MY_TABLE_FIELD) inside the select clause in the Linq expression.
I have read about methods that converts value, but i get error. And I don't want to use lambda expressions.
What can I do?
Supposing your values are actually convertible to longs, you should be able to do this:
from context.SOME_TABLE
where ...
select Convert.ToInt64(row.MY_TABLE_FIELD)
If the values are not convertible to longs, you might want to consider selecting out the string from the database, and then post-processing the values based on more complex business logic.
(If this doesn't work, provide the error message that you're getting, and more details on your database, etc.)
Thanks StriplingWarrior for your answer.
I´ve found a solutions few minutes after i posted the question.
Here I share it.
LINQ
var MyList = (from ta in model.Table
select new MyClass
{
StringFieldID = ta.StringFieldID,
OtherField = ta.OtherField
}).ToList();
And the work i did it on the class MyClass Code
public class MyClass
{
private string _StringFieldID;
public long LongFieldID { set; get; }
public string OtherField{set;get;}
public string StringFieldID{
set
{
_StringFieldID= value;
LongFieldID= long.Parse(_StringFieldID);
}
get
{
return _StringFieldID;
}
}
}
So, every time the Linq Query sets a value for StringFieldID is the class who makes the casting.
Related
I've written the below function to query the SQLite DB in my Xamarin forms app. But, since I have to call .ToList() twice, I'm not very confident about it. Is this bad code? Any feedback will be highly appreciated.
public static List<string> GetAllLocationIds()
{
try
{
lock (CollisionLock)
{
//TableQuery<TResult> First .ToList() result
//List<string> Second .ToList() result
return Database.Table<Location>().ToList().Select(loc=>loc.LocationId).ToList();
}
}
catch (Exception ex)
{
Insights.Report(ex);
return null;
}
}
Performing .Select directly on Database.Table<Location>() results in the following exception.`
System.MissingMethodException: Default constructor not found for type
System.String at System.RuntimeType.CreateInstanceMono`
Yes it is.
On
Database.Table<Location>().ToList()
You are materializing all of Table Location. Then you are only selecting the LocationId in Memory.
Instead use:
Database.Table<Location>().Select(loc=>loc.LocationId).ToList();
Which is working directly on IQueryable<Location> and only materializes the LocationId. Assuming Table<Location> is IQueryable<Location>.
You just can not do Linq projections to string types like that with sqlite-net(-pcl) as it needs a default parameterless constructor.
What follows is the "best way" simulate a "Linq projection" that I have found when Mobile memory and performance is considered.
Use a custom Class with only the columns that need projected
Use a SQL query with only the columns needed to map to that custom class (where filter in the select statement if needed)
Convert to custom type
Actual Table Class:
class Location
{
[PrimaryKey]
public int Column1 { get; set; }
public int Column2 { get; set; }
~~~
public string LocationId { get; set; }
}
Now make a new class that describes your "projection" needs, in this case I only want the LocationId column.
Projection Class
class SimpleList
{
public string LocationId { get; set; }
}
SQL Select (selecting only the columns that map to the projection class)
SQLiteConnection.Query<SimpleList>("select LocationId from [Location]")
Now you have a List<SimpleList>, you can convert it to a List<string> if you really need to:
SQLiteConnection.Query<SimpleList>("select LocationId from [Location]").ConvertAll(x => x.LocationId);
Is it worth it? If you have a large number of rows and/or columns in your table and cannot use a deferred query and/or avoid a Linq projection... IMHO yes... Use the profiler to confirm ;-)
If you have a couple dozen rows? Maybe not, but even then the number of temp. objects that get instanced is reduced, and for me that is a win on mobile.
Is it possible to convert dynamic variable returned from Dapper.Query to object? Example:
class MyClass
{
public int Id { get; set;}
public string SomeData { get; set; }
}
IEnumerable<dynamic> data = sql.Query<dynamic>(SELECT * FROM MyClassTable");
foreach(var record in data)
{
int parent_id = record.SomeId;
// any simple way to fill MyClass properites from record?
// instead of doing in manually?
MyClass obj = ?
}
any simple way to fill MyClass properites from record?
instead of doing in manually?
No. Unfortunately you'll have to do it manually. The best thing you can do is move the mapping logic to one place so you can re-use it in your data access layer. I usually use extension methods, but you can also use reflection to set matching property names...however, in my personal opinion, I usually use dapper for its performance benefits and using reflection is a bit expensive, for that reason I usually avoid using reflection in these cases.
Is it possible to convert dynamic variable returned from Dapper.Query to object?
Yes. As explained above
I know you probably wanted to simplify your question but I guess it's worth pointing out that the example that you posted doesn't need to return a dynamic object if MyClass represents the records returned by that select query. You could simply do the following...
var data = sql.Query<MyClass>(SELECT * FROM MyClassTable");
Yesterday I was working on a code refactor and came across an exception that I really couldn't find much information on. Here is the situation.
We have an a pair of EF entities that have a many to many relationship through a relation table. The objects in question look like this, leaving out the unnecessary bits.
public partial class MasterCode
{
public int MasterCodeId { get; set; }
...
public virtual ICollection<MasterCodeToSubCode> MasterCodeToSubCodes { get; set; }
}
public partial class MasterCodeToSubCodes
{
public int MasterCodeToSubCodeId { get; set; }
public int MasterCodeId { get; set; }
public int SubCodeId { get; set; }
...
}
Now, I attempted to run a LINQ query against these entities. We use a lot of LINQ projections into DTOs. The DTO and the query follow. masterCodeId is a parameter passed in.
public class MasterCodeDto
{
public int MasterCodeId { get; set; }
...
public ICollection<int> SubCodeIds { get; set; }
}
(from m in MasterCodes
where m.MasterCodeId == masterCodeId
select new MasterCodeDto
{
...
SubCodeIds = (from s in m.MasterCodeToSubCodes
select s.SubCodeId).ToList(),
...
}).SingleOrDefaultAsync();
The internal query throws the following exception
Expression of type 'System.Data.Entity.Infrastructure.ObjectReferenceEqualityComparer' cannot be used for constructor parameter of type 'System.Collections.Generic.IEqualityComparer`1[System.Int32]'
We have done inner queries like this before in other places in our code and not had any issues. The difference in this one is that we aren't new-ing up an object and projecting into it but rather returning a group of ints that we want to put in a list.
I have found a workaround by changing the ICollection on MasterCodeDto to IEnumerable and dropping the ToList() but I was never able to find out why I couldn't just select the ids and return them as a list.
Does anyone have any insight into this issue? Normally returning just an id field and calling ToList() works fine when it is not part of an inner query. Am I missing a restriction on inner queries that prevents an operation like this from happening?
Thanks.
Edit: To give an example of where this pattern is working I'll show you an example of a query that does work.
(from p in Persons
where p.PersonId == personId
select new PersonDto
{
...
ContactInformation = (from pc in p.PersonContacts
select new ContactInformationDto
{
ContactInformationId = pc.PatientContactId,
...
}).ToList(),
...
}).SingleOrDefaultAsync();
In this example, we are selecting into a new Dto rather than just selecting a single value. It works fine. The issues seems to stem from just selecting a single value.
Edit 2: In another fun twist, if instead of selecting into a MasterCodeDto I select into an anonymous type the exception is also not thrown with ToList() in place.
I think you stumbled upon a bug in Entity Framework. EF has some logic for picking an appropriate concrete type to materialize collections. HashSet<T> is one of its favorites. Apparently (I can't fully follow EF's source code here) it picks HashSet for ICollections and List for IEnumerable.
It looks like EF tries to create a HashSet by using the constructor that accepts an IEqualityComparer<T>. (This happens in EF'sDelegateFactory class, method GetNewExpressionForCollectionType.) The error is that it uses its own ObjectReferenceEqualityComparer for this. But that's an IEqualityComparer<object>, which can not be converted to an IEqualityComparer<int>.
In general I think it is best practice not to use ToList in LINQ queries and to use IEnumerable in collections in DTO types. Thus, EF will have total freedom to pick an appropriate concrete type.
Let's say we have a class that has an int array as one of its properties.
public MyClass
{
public int Id { get; set; }
public string Name { get; set; }
public int[] Values { get; set; }
}
We store it in the db using entity framework. What I've noticed is that EF turns the Values property into a varchar(max).
Now, what we would want to do, is to query the database and return records for which 'Values' contains the given int parameter.
public List<MyClass> Search(int valueToSearch)
{
return context.MyClasses.Where(x => x.Values.Contains(valueToSearch)).ToList();
}
However, this throws an exception that linq-to-sql does not support the contains command.
I also tried Where(x => x.Values.Any(y => y == valueToSearch)) but that throws the same exception.
I suppose it has something to do with the fact that EF turns the array into a varchar so it won't be able to use the contains statement or something. Maybe a seperate table for the Values would solve the problem, but it looks kinda stupid to create a class that only has one integer property?
Is there a better way to solve this?
I found a lot of examples doing the opposite (the SQL IN statement) but that's not what we're looking for. We only have one integer as parameter, but several integers in our entity.
We're using EF5 with .NET 4.0.
Thanks!
Edit
It seems the varchar is a string we create ourselves. As it was code I didn't write myself, I wasn't aware of that. That string ofcourse gets translated into a varchar.
So now the question changes into something like 'What's the best way to store arrays of primitive types?' and that question has already been answered many times here on SO (one of them is provided in the comments).
Do the query outside of linq to entities: (you have to pull in all the rows)
public List<MyClass> Search(int valueToSearch)
{
var c = context.MyClasses.ToList(); // cache
return c.Where(x => x.Values.Contains(valueToSearch));
}
EDIT: If you are currently manually converting the array of integers into a string, then change your class property from an array of integers to a string.
Although, I recommend a new table. An array of integers as a string in a db fields smells a little.
I have a datacontext, and it has Authors table.
public partial Author:IProductTag{}
I want to cast Table<Authors> object to Table<IProductTag>, but that appears to be impossible. I am trying to do that because I want my method to be able to work with different tables which come as input parameters. To be more specific, I need to execute OrderBy and Select methods of the table. I have few other tables, entities of which implement IProductTag . Also, I tried to write a function like:
public static void MyF<t>(){
Table<t> t0 = (Table<t>)DataContext.GetMyTableUsingReflection();
}
But it fails at compile-time. And if I cast the table to something like ITable or IQueriable, then the OrderBy and Select functions simply don't work. So how do you deal with it?
I suspect you want to make your method generic - so instead of
void DoSomethingWithTable(Table<IProductTag> table)
you should have
void DoSomethingWithTable<T>(Table<T> table) where T : class, IProductTag
That should work fine, assuming you only need to read entities (and apply query operators etc). If that doesn't work for you, please give more details about what your method needs to do.
(You say that your attempt to use reflection failed, but you haven't said in what way it failed. Could you give more details?)
I have no idea what a ProductTag is so I've used different types to show my solution to this problem. Yes there doesn't seem to be a way to get a Table<T>, but you can get IQueryable<T> which works just as well (at least for my situation).
I have a simple analytics database, where each website has its own table containing both generic and specific items. I wanted to use an interface for the shared data.
public interface ISession
{
public DateTime CreateDt {get; set; }
public string HostAddress {get; set; }
public int SessionDuration {get; set; }
}
public static IQueryable<ISession> GetQueryableTable(MyDataContext db, string site)
{
Type itemType;
switch (item)
{
case "stackoverflow.com":
itemType = typeof(Analytics_StackOverflow);
break;
case "serverfault.com":
itemType = typeof(Analytics_ServerFault);
break;
default: throw Exception();
}
return db.GetTable(itemType).Cast<ISession>();
}
You can then do a query like this :
var table = GetQueryableTable(db, "stackoverflow.com");
var mySessions = table.Where(s => s.HostAddress == MY_IP);
To create a new row you can use reflection :
var rowType = typeof(Analytics_ServerFault);
var newRow = (ISession) rowType.GetConstructor(new Type[0]).Invoke(new object[0]);
(I have a function to get GetRowType - which is not shown here).
Then to insert into the table I have a separate helper function:
public static void Insert(MyDataContext db, ISession item)
{
// GetTable is defined by Linq2Sql
db.GetTable(GetRowType(domain)).InsertOnSubmit(item);
}