How to use predicate search with Linq-to-SQL - sql-server

We are using Linq-to-SQL with SQL Server as an ORM on our new project. I have never used Linq-to-SQL before, so my question can be a bit dumb. I want to have a method that will perform a search for entities in DB by predicate, something like this:
public IEnumerable<T> Get<T>(Expression<Func<T, bool>> #where)
Could you give some advices where I can see some code samples or ideas how to implement this?

You want Where (here, originalList is any enumerable, in particular it can be a table from your context):
var filteredList = originalList.Where(element => ShouldBeIncluded(element));
Edit:
or
var filteredList =
from element in originalList
where ShouldBeIncluded(element)
select element;
And if ShouldBeIncluded is a Func<T, bool>, there's an eye-candy simplified syntax:
var filteredList = originalList.Where(ShouldBeIncluded);
Edit 2: also, note that the complete syntax is:
var filteredList = originalList.Where<TTypeOfElement>(element => ShouldBeIncluded(element));
But the generic argument can be omitted since the compiler will deduce it from the type of originalList (supposing it is an IEnumerable<TTypeOfelement>).

Related

Writing a generic InsertData method using Dapper.Contrib and InsertAsync

I am fairly new to C# and Dapper. I am trying to write a generic Insert Data method for my project. I come from a Delphi environment so I am still finding my way around C#.
Dapper seems fairly straight forward to use but I am experiencing some challenges. I have tried every which way to get the syntax right with the following code but have been unsuccessful.
Issues seem to be around the T (I still quite don't understand what T is) and all the combinations I have tried don't work.
public async Task<int> InsertData<T>(T list)
{
string connectionString = _config.GetConnectionString(ConnectionStringName);
using (IDbConnection connection = new SqlConnection(connectionString))
{
return await connection.InsertAsync<int>(list);
}
}
The following code does work, so where am I going wrong?
public async Task SaveData<T>(string sql, T parameters)
{
string connectionString = _config.GetConnectionString(ConnectionStringName);
using (IDbConnection connection = new SqlConnection(connectionString))
{
await connection.ExecuteAsync(sql, parameters);
}
}
Your second code (await connection.ExecuteAsync(sql, parameters);) works because you are simply executing your hand written SQL statement. The method ExecuteAsync belongs to Dapper; NOT Dapper.Contrib. The generic T is used for parameters, not for the object you are trying to insert.
With your first code (return await connection.InsertAsync<int>(list);), you are actually using Dapper.Contrib; you are not writing the SQL statement by hand. Dapper.Contrib generates it for you.
Your following code seems the problem:
return await connection.InsertAsync<int>(list);
You are passing generic parameter <int> to the method which does not make sense.
I have not tested this but I hope changing that line to below one should work:
return await connection.InsertAsync<T>(list);
Also, you have to make sure the generic type T is class by adding where T : class to it.
Following generic method should serve your purpose; you need to convert it to async to match with your current code:
public void InsertData<T>(T entity) where T : class
{
string connectionString = ....;
using(IDbConnection connection = new SqlConnection(connectionString))
{
long result = connection.Insert<T>(entity);
}
}
I did not understood few other parts in your code. Say InsertData<T>(T list). What is list? Is it as single object or list of objects? If it is list of objects, List<T> list makes more sense. If it is single object, better you rename list to actual object name, say T customer/entity/poco etc.

adding new methods to LINQ to Entities

Is there any way to define the SQL conversion component for additional functions to Linq2Entities.
For example:
myQuery.Where(entity => entity.Contains('foo', SearchFlags.All))
Ideally I am looking for something that doesn't require editing and building a new version the EntityFramework.dll directly. Is there any way to allow extension methods to entity framework that can support SQL generation.
So far I have a template which would represent the method I need to replace for LINQ to Entities:
public static bool Contains(this object source, string searchTerms, SearchFlags flags)
{
return true;
}
Of course this causes the error:
LINQ to Entities does not recognize the method 'Boolean
CONTAINS(System.Object, System.String, SearchFlags)' method, and this method
cannot be translated into a store expression.
To be clear, I don't want to do:
myQuery.AsEnumerable().Where(entity => entity.Contains('foo', SearchFlags.All))
Because I want to be able to execute code in SQL space and not return all the entities manually.
I also cannot use the .ToString() of the IQueryable and execute it manually because I need Entity Framework to populate the objects from several .Include joins.
I don't understand your Q clearly. However if your problem is that you can't use your own methods or other linq to objects method, just use .AsEnumerable() and do your other jobs through linq to objects, not L2E:
myQuery.AsEnumerable().Where(entity => entity.Contains('foo', SearchFlags.All))
And if you need to use your myQuery several times somewhere else, first load it to memory, then use it as many as you want:
var myQuery = from e in context.myEntities
select d;
myQuery.Load();
// ...
var myOtherQuery = from d in context.myEntities.Local
select d;
// Now any L2O method is supported...
I ended up doing the following (which works but is very far from perfect):
All my entities inherit from an IEntity which defines long Id { get; set; }
I then added a redundant restriction
context.myEntities.Where(entity => entity.Id != 0) this is
redundant since the identity starts at 1, but Linq2Entities doesn't
know that.
I then call .ToString() on the IQueryable after I have done all
my other queries, since it is of type DBQuery<Entity> it returns
the SQL Command Text, I do a simple replace with my query restriction.
In order to get all the .Include(...) to work I actually execute
two different sql commands. There is no other more pretty way to tap into this because of query execution plan caching causes issues otherwise (even when disabled).
As a result my code looks like this:
public IQueryable<IEntity> MyNewFunction(IQueryable<IEntity> myQueryable, string queryRestriction)
{
string rawSQL = myQueryable.Select(entity => entity.Id).ToString().Replace("[Extent1].Id <> 0", queryRestriction);
List<long> ids = // now execute rawSQL, get the list of ids;
return myQuerable.Where(entity => ids.Contains(entity.Id));
}
In short, other than manually executing the SQL or running a similar SQL command and appending the restriction using the existing commands the only way to write your own methods to Linq-to-Entities is to manually alter and build your own EntityFramework.dll from the EF6 source.

EF ObjectQuery<T> Context, Parameters, Connection properties equivalent on DbSet<T>

In the earlier versions of Entity Framework, we were able to reach the Context out of ObjectQuery in order to read Parameters, Connection, etc. as below:
var query = (ObjectQuery<T>)source;
cmd.Connection = (SqlConnection)((EntityConnection)query.Context.Connection).StoreConnection;
cmd.Parameters.AddRange(
query.Parameters.Select(x => new SqlParameter(
x.Name, x.Value ?? DBNull.Value)
).ToArray()
);
When I look at the DbSet<T> object, I am unable to find any equivalent of this. My purpose here is to create extensions which will manipulate the query and get the result out of it.
Here is an instance: http://philsversion.com/2011/09/07/async-entity-framework-queries
Or should I write the extension for DbContext class and work with Set method?
Any idea?
Edit
Here is what I did so far. Basic implementation so far but certainly not ready for production. Any suggestions on this?
public static async Task<IEnumerable<T>> QueryAsync<T>(this DbContext #this, System.Linq.Expressions.Expression<Func<T, bool>> predicate = null)
where T : class {
var query = (predicate != null) ? #this.Set<T>().Where(predicate) : #this.Set<T>();
var cmd = new SqlCommand();
cmd.Connection = (SqlConnection)(#this.Database.Connection);
cmd.CommandText = query.ToString();
if (cmd.Connection.State == System.Data.ConnectionState.Closed) {
cmd.Connection.ConnectionString = new SqlConnectionStringBuilder(cmd.Connection.ConnectionString) {
AsynchronousProcessing = true
}.ToString();
cmd.Connection.Open();
}
cmd.Disposed += (o, e) => {
cmd.Clone();
};
var source = ((IObjectContextAdapter)#this).ObjectContext.Translate<T>(
await cmd.ExecuteReaderAsync()
);
return source;
}
This is a nice workaround, although I don't think you can make it much more generally applicable than what you already have.
A few things to keep in mind:
- Depending on the EF query, e.g. if you are using Include or not, the columns returned in the reader might not match the properties in the type T you are passsing.
- Depending on whether you have inheritance in your model, the T that you pass to translate may not always be the right thing to materialize for every row returned.
- After the task returned by ExecuteReaderAsync completes, you still have to retrieve each row, which depending on the execution plan for the query and the latency you are getting with the server is potentially also a blocking operation.
Async support is not coming to EF in 5.0 but we worked with other teams to make sure we have all the necessary building blocks included in .NET 4.5 and the feature is pretty high in our priority list. I encourage you to vote for it in our UserVoice site.

Dapper Correct Object / Aggregate Mapping

I have recently started evaluating Dapper as a potential replacement for EF, since I was not too pleased with the SQL that was being generated and wanted more control over it. I have a question regarding mapping a complex object in my domain model. Let's say I have an object called Provider, Provider can contain several properties of type IEnumerable that should only be accessed by going through the parent provider object (i.e. aggregate root). I have seen similar posts that have explained using the QueryMultiple and a Map extension method but was wondering how if I wanted to write a method that would bring back the entire object graph eager loaded, if Dapper would be able to do this in one fell swoop or if it needed to be done piece-meal. As an example lets say that my object looked something like the following:
public AggregateRoot
{
public int Id {get;set;}
...//simple properties
public IEnumerable<Foo> Foos
public IEnumerable<Bar> Bars
public IEnumerable<FooBar> FooBars
public SomeOtherEntity Entity
...
}
Is there a straightforward way of populating the entire object graph using Dapper?
I have a similar situation. I made my sql return flat, so that all the sub objects come back. Then I use the Query<> to map the full set. I'm not sure how big your sets are.
So something like this:
var cnn = sqlconnection();
var results = cnn.Query<AggregateRoot,Foo,Bars,FooBar,someOtherEntity,AggregateRoot>("sqlsomething"
(ar,f,b,fb,soe)=>{
ar.Foo = f;
ar.Bars = b;
ar.FooBar = fb;
ar.someotherentity = soe;
return ar;
},.....,spliton:"").FirstOrDefault();
So the last object in the Query tag is the return object. For the SplitOn, you have to think of the return as a flat array that the mapping will run though. You would pick the first return value for each new object so that the new mapping would start there.
example:
select ID,fooid, foo1,foo2,BarName,barsomething,foobarid foobaritem1,foobaritem2 from blah
The spliton would be "ID,fooid,BarName,foobarid". As it ran over the return set, it will map the properties that it can find in each object.
I hope that this helps, and that your return set is not too big to return flat.

Query a winforms control with CheckedListBoxItemCollection.AsQueryable().Provider.CreateQuery(

I need to query a winforms control(CheckedListBoxControl) having a CheckedListBoxItemCollection which I want to query for a certain Id that is within the property "Value" of the CheckedListBoxItem.
CheckedListBoxItemCollection items = myCheckedListBoxControl.Items;
foreach(Department dep in departmentList)
{
bool isDepExisting = items.AsQueryable().Where( the .Where clause does not exist );
// How can I query for the current dep.Id in the departmentList and compare this dep.Id with every Item.Value in the CheckedListBoxControl and return a bool from the result ???
if(!isDepExisting)
myCheckedListBoxControl.Items.Add( new CheckedListBoxItem(dep.id);
}
UPDATE:
IEnumberable<CheckedListBoxItem> checks = items.Cast<CheckedListBoxItem>().Where(item => item.Value.Equals(dep.InternalId));
Why says Visual Studio that the IEnumerable or the IEnumberable namespace of it can not be found? When I use "var" instead then I can compile my code. But the boss in my company forbide my to use var...
CheckListBox.Items only implements IEnumerable, not IEnumerable<T>. You get the overload of AsQueryable() that returns IQueryable (not the generic one). Which only has the Cast and OfType extension methods.
Cast the items back from object to Department. Like this:
var q = checkedListBox1.Items.Cast<Department>().AsQueryable().Where((d) => d.Id == 1);
You don't need AsQueryable() anymore btw.

Resources