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.
Related
I have a part of my application which querys a database for records. One of the fields in this query is a string type status of known values:
Open, Closed, Cancel
The user has 3 check boxes and can select any combination to determine which types of records they get back. So in my view model I have a status filter property with 3 bools:
public class SalesOrderStatusFilter
{
public bool Open { get; set; }
public bool Closed { get; set; }
public bool Canceled { get; set; }
}
Now when a query is run, I'd like to filter the results based on the chosen status types. Right now I've got a linq query like this:
public IEnumerable<SalesOrders> GetSalesOrders(SalesOrderParams parameters)
{
return _dbContext.SalesOrderLookup()
.Where(x => (x.Status.EqualsTrim("Open") && parameters.SalesOrderStatusFilter.Open)
|| (x.Status.EqualsTrim("Closed") && parameters.SalesOrderStatusFilter.Closed)
|| (x.Status.EqualsTrim("Cancel") && parameters.SalesOrderStatusFilter.Canceled)).ToList();
}
This is a common pattern across my application and I'd like to find a better solution that I can reuse without having to keep typing out the query every time. I've already tested out converting my db string statuses to enums using some custom attributes, reflection, etc, but I'm worried it's a bit overkill when I'm doing mostly view-only type querys for these various reports, so I'm not sure I'm going to stick with it. It's also added a bit of a performance hit to do the enum conversion (The enum values didn't always match the database values, so thats why I was using reflection and custom attributes).
Can anybody recommend a good approach to dealing with this problem?
Edit:
for clarity, the SalesOrderStatusFilter is a property of another
public class SalesOrderParams
{
public string SalesOrderNumber { get; set; }
public SalesOrderStatusFilter SalesOrderStatusFilter { get; set; }
}
I think the main challenge I'm trying to solve is mapping the bools to their string equivalents, which may not always match by name (sometimes theres a space, for example), and then making a more concise and reusable call.
Please try like this
First compare with status again compare with parameters status... (&& replace for ==)
_dbContext.SalesOrderLookup()
.Where(x => (x.Status.EqualsTrim("Open") == parameters.Open)
|| (x.Status.EqualsTrim("Closed") == parameters.Closed)
|| (x.Status.EqualsTrim("Cancel") == parameters.Canceled)).ToList();
Why you don't use bool type? Even if you use enums and define it as byte type, it takes 1-byte length for each record while using 3 bool type, takes 3-bit length. Also the performance of checking one bit is higher than comparing a string. So I think better choice for you is defining three bool type for status variables as you define check boxes. Also in this way you will be able to read from and writ to db directly without any data conversion.
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.
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.