Dynamic ObjectQuery<T> against EF 4 using parsed Entity SQL - c#

I am trying to create a completely dynamic way to query entity framework using entity sql, where the type T in ObjectQuery (the table name, or entity name) is not known at compile time, and is passed into a method as a string,
public class EntityQuery
{
public ObjectContext Context { get; private set; }
public string TableName { get; private set; }
public EntityQuery(ObjectContext context, string sourceObject)
{
MemberInfo[] mInfo = context.GetType().GetMembers();
TableName = sourceObject;
Context = context;
if (!mInfo.Any<MemberInfo>(MemberInfo => MemberInfo.Name == TableName))
throw new EntityOperationException("Entity '" + TableName + "' does not exist in the object context.");
}
public ObjectQuery<dynamic> InitiateQuery(List<EntityFilter> filters)
{
string predicate = DynamicESQLBuilder.GetESQL(filters, Context.DefaultContainerName, TableName);
string format = String.Format("[{0}]", predicate);
ObjectQuery<dynamic> query = new ObjectQuery<dynamic>(predicate, Context, MergeOption.NoTracking);
return query;
}
}
The class DynamicESQLBuilder creates an entity sql command string based on the context name, table name, and entity filters passed in. The 'EntityFilter' class essentially encapsulates a single 'where' clause,
public class EntityFilter
{
public ExpressionType OpType { get; private set; }
public string PropertyName { get; private set; }
public object Value { get; private set; }
}
When I try to use it like this,
MyDBModel db = new MyDBModel();
List<EntityFilter> filters = new List<EntityFilter>()
{
new EntityFilter("Name", ExpressionType.Equal, "Sean")
};
EntityQuery query = new EntityQuery(db, "Person");
var results = query.InitiateQuery(filters);
I get an error stating:
'Name' is not a member of type 'MyDBModel.Person' in the currently loaded schemas. Near simple identifier, line 1, column 74.
I am open to an entirely different way of doing this. But basically what I am trying to achieve is similar to what WCF OData services do behind the scenes -- I can query my model with just strings passed in - the name of the entity, the operation, the value, etc. No need for a strongly typed 'T' in my query.

If you have Entity SQL as a string and you don't mind doing things the old-school ADO.NET way, you can always use EntityConnection, EntityCommand, and EntityDataReader directly.

Related

How do you call a stored procedure in EF Core and return the results using a generic class? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 days ago.
This post was edited and submitted for review 3 days ago.
Improve this question
How can you call a stored procedure using Entity Framework Core and return the results using a generic class?
In .NET Standard Entity Framework, I'm able to call stored procedures and return generic like so:
public IEnumerable<T> ExecuteStoredProcedure<T>(object[] sqlParameters) where T : IStoredProcedure, new()
{
if (sqlParameters == null) sqlParameters = new object[] { };
return DataContext.Database.SqlQuery<T>((new T()).Query, sqlParameters).ToList();
}
This method is not available in the same fashion in Entity Framework Core anymore...
Assuming you're using Code First and are comfortable with the steps necessary to create the actual stored procedure and have that defined in your database, I will focus my answer on how to call that stored procedure in C# and map the results generically.
First you want to create a model that matches the data you expect to get back from your results.
Here is an example:
public class UserTimesheet : IStoredProcedure
{
public string Query => "[dbo].[GetUserTimesheet] #userId, #month, #year";
public DateTime WorkDate { get; set; }
public Guid ProjectId { get; set; }
public int CategoryId { get; set; }
public string? ProjectName { get; set; }
public decimal? Hours { get; set; }
}
Notice this extends an interface called IStoredProcedure with the Query property. More on that later, but it's there to work by convention.
Here is that interface:
public interface IStoredProcedure
{
string Query { get; }
}
Next you'll want to add a DbSet to your database context.
// Put this in your Database Context
public DbSet<UserTimesheet> UserTimesheets { get; set; } = null!;
Now since this doesn't map to an actual table, you will want to add some code to the OnModelCreating to tell EF how to reference it. Again, I'm working on a convention, in this case I only want to apply this setting to models that implement IStoredProcedure, and we can do that with a little reflection to make life easier.
In this case we're going to say it has no key and treat it like a view. I created an extension method to keep things a little cleaner, you can use it like this:
public static class ModelBuilderExtension
{
public static ModelBuilder ConfigureStoredProcedureDbSets(this ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
if (typeof(IStoredProcedure).IsAssignableFrom(entityType.ClrType))
{
modelBuilder.Entity(entityType.ClrType).HasNoKey().ToView(null);
}
}
return modelBuilder;
}
}
// Put this in DatabaseContext class
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ConfigureStoredProcedureDbSets();
}
Next you'll want something to hold your generic code. I use a class called StoredProcedureRepository, but you can call it what you like. Here's the code, including the interface (for Dependency Injection, if you like):
public abstract class Repository
{
protected readonly DatabaseContext _context;
protected Repository(DatabaseContext context)
{
_context = context;
}
}
public interface IStoredProcedureRepository
{
IEnumerable<T> ExecuteStoredProcedure<T>(object[] sqlParameters) where T : class, IStoredProcedure, new();
SqlParameter GetSqlParameter(string name, object value, bool isOutput = false);
}
public class StoredProcedureRepository : Repository, IStoredProcedureRepository
{
#region Properties
private const string SQL_PARAMETER_PREFIX = "#";
#endregion
#region Constructor
public StoredProcedureRepository(DatabaseContext context) : base(context)
{
}
#endregion
#region Shared Public Methods
public IEnumerable<T> ExecuteStoredProcedure<T>(object[] sqlParameters) where T : class, IStoredProcedure, new()
{
return _context.Set<T>().FromSqlRaw<T>((new T()).Query, sqlParameters).ToList();
}
public SqlParameter GetSqlParameter(string name, object value, bool isOutput = false)
{
if (!name.StartsWith(SQL_PARAMETER_PREFIX))
{
name += SQL_PARAMETER_PREFIX;
}
var direction = isOutput ? System.Data.ParameterDirection.Output : System.Data.ParameterDirection.Input;
return new SqlParameter
{
ParameterName = name,
Value = value,
Direction = direction
};
}
#endregion
}
There are a few things to notice here. It's referencing the DbSet generically and using FromRawSql and calling the Query (string) property from the IStoredProcedure implementation of your model. So you'll want to make sure that contains your query to execute the stored procedure, in this example that would be "[dbo].[GetUserTimesheet] #userId, #month, #year"
Now you can call this stored procedure generically.
Here is an example:
var parameters = new object[3];
parameters[0] = GetSqlParameter("#userId", userId);
parameters[1] = GetSqlParameter("#month", month);
parameters[2] = GetSqlParameter("#year", year);
IList<UserTimesheet> queryResults = _storedProcedureRepository.ExecuteStoredProcedure<UserTimesheet>(parameters).ToList();
To add new stored procedures, just create their respective models (being sure to implement IStoredProcedure and define their Query property, then add their DbSet to the database context.
For example:
public class UserProject : IStoredProcedure
{
public string Query => "[dbo].[GetUserProjects] #userId";
public Guid ProjectId { get; set; }
public string ProjectName { get; set; }
}
// add this to the database context
public DbSet<UserProject> UserProjects { get; set; } = null!;
then call it like so:
var parameters = new object[1];
parameters[0] = GetSqlParameter("#userId", userId);
IList<UserProject> queryResults = _storedProcedureRepository.ExecuteStoredProcedure<UserProject>(parameters).ToList();

How to correctly store a number of results in a DTO?

I have stored procedure attached to a DB which should return results from just a simple search. The query is added to my entity and calls a regular method. The problem I face is storing the results from this procedure to a particular DTO as a list.
Is there any way to effectively store the results from this stored procedure as a list to the DTO?
Below is what I have so far
Controller:
[Produces("application/json")]
[RoutePrefix("api/jobs")]
public class OutputController : ApiController
{
private TestCoastalToolsEntities _output;
public OutputController()
{
_output = new TestCoastalToolsEntities();
_output.Configuration.ProxyCreationEnabled = false;
}
/**Search**/
// POST: api/postsearch
[System.Web.Http.HttpPost, System.Web.Http.Route("postsearch")]
public async Task<IHttpActionResult> PostSearch(SearchInputDTO srequest)
{
OutputDTO<SearchInputDTO> output = new OutputDTO<SearchInputDTO>();
SearchInputDTO SearchInput = null;
var searchString = srequest.SearchValue.ToString();
SearchInput.Results = _output.searchLog2(searchString);
if (_oput != null)
{
output.Success = true;
output.Results = _SearchInput.Results;
var json = new JavaScriptSerializer().Serialize(output);
return Ok(json);
}
return Ok(_ot);
}
}
}
-------------------------------------
Search DTO:
namespace toolPortal.API.Data.DTO
{
public class SearchInputDTO
{
public List<object> Results { get; set; }
public SearchInputDTO(output output) {
this.ID = output.ID;
this.Name = output.Name;
this.Job = output.Job;
this.Start = output.Start;
this.End = output.End;
this.Logs = output.Logs;
}
}
}
The expected result is that the stored procedure runs and stores the list of results to SearchInputResults. From there, those results should be stored in another DTO to be passed off on the return.
With EF you will want to leverage Select() to map the entities to your DTO, though you will need to consider the entire structure of the DTO. For instance, what is the "Logs" data structure going to comprise of? Is it a single string value, a list of strings, or a list of log records?
Using Select() you need to leverage property setters, not a constructor accepting an entity.
So a pattern like this:
public class Entity
{
public string Field { get; set; }
}
public class Dto
{
public string Field { get; set; }
}
var dtos = context.Entities
.Where(x => x.IsActive)
.Select(x => new Dto
{
Field = x.Field
})
.ToList();
Looking at your example with the constructor:
public class Dto
{
public string Field { get; private set; }
public Dto(Entity entity)
{
Field = entity.Field;
}
}
var dtos = context.Entities
.Where(x => x.IsActive)
.Select(x => new Dto(x))
.ToList();
This doesn't work with EF & Select. EF can map to an object, but only via properties and a parameterless constructor. There is a hack around this to be aware of, but avoid if you do see it:
var dtos = context.Entities
.Where(x => x.IsActive)
.ToList()
.Select(x => new Dto(x))
.ToList();
With the extra ToList() before the select, the call will work because EF will execute the query and return the list of entities, then the Select() will be performed as a Linq2Object query. The reason you should avoid this is because EF will select all properties from the entity, where we should only pull back the properties we care about. It's also easy to fall into a lazy-load performance trap if your Dto constructor population starts iterating over related entities. Using Select to load just the fields you need from an entity and any related entities allows EF to build an efficient query for just the data needed without any lazy load traps.
Using AutoMapper you can simplify this by setting up the mapping from entity to DTO then leveraging ProjectTo<Dto>().
So, if you want a DTO to represent the results (such as a success flag, error message) with a collection of the results if successful:
[Serializable]
// Our results container.
public class SearchResultsDTO
{
public bool IsSuccessful { get; private set; } = false;
public string ErrorMessage { get; private set; }
public ICollection<SearchResultDTO> Results { get; private set; } = new List<SearchResultDTO>();
private SearchResultsDTO() {}
public static SearchResultsDTO Success(ICollection<SearchResultDTO> results)
{
var results = new SearchResultsDTO
{
IsSuccessful = true,
Results = results
};
return results;
}
public static SearchResultsDTO Failure(string errorMessage)
{
var results = new SearchResultsDTO
{
ErrorMessage = errorMessage
};
return results;
}
}
[Serializable]
public class SearchResultDTO
{
public int ID {get; set;}
public string Name {get; set;}
public string Job {get; set;}
public DateTime Start {get; set;}
public DateTime End {get; set;}
public ICollection<string> Logs {get; set;} = new List<string>();
}
then to populate these from a DbContext: (Inside a Repository or wherever reads the data)
using (var context = new SearchContext())
{
var results = context.Logs
.Where(x => x.Name.Contains(sRequest))
.Select(x => new SearchResultDTO
{
ID = x.ID,
Name = x.Name,
Job = x.Job,
Start = x.Start,
End = x.End,
Logs = x.LogLines.Select(y => y.Line).ToList(),
}).ToList();
var resultDto = SearchResultsDTO.Success(results);
return resultsDto;
}
This assumes that the log entry has a Job, name, start, end date/times, and then a list of "lines" or entries to display as "Logs". (Where the Log table has a related LogLine table for example with the one or more lines) This demonstrates how to leverage Select to map not only the log record into a DTO, but also to map related records into something like a collection of strings, or a collection of other DTOs can be done as well.
Once it selects the DTO, I have it fill a container DTO using static factory methods to populate either a successful read, or a failed read. (which can be set in an exception handler for example.) Alternatively you can just new up a container class and populate properties, use a constructor /w parameters, or just return the list of DTOs. The SearchResultsDTO container is not referenced within the EF query.

Generic repository with Dapper

I'm trying to build a generic repository with Dapper. However, I have some difficulties to implement the CRUD-operations.
Here is some code from the repository:
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
internal IDbConnection Connection
{
get
{
return new SqlConnection(ConfigurationManager.ConnectionStrings["SoundyDB"].ConnectionString);
}
}
public GenericRepository(string tableName)
{
_tableName = tableName;
}
public void Delete(TEntity entity)
{
using (IDbConnection cn = Connection)
{
cn.Open();
cn.Execute("DELETE FROM " + _tableName + " WHERE Id=#ID", new { ID = entity.Id });
}
}
}
As you can see, my delete-method takes a TEntity as parameter which is a parameter of type class.
I call my Delete-method from my UserRepository like this:
public class UserRepository : GenericRepository<User>, IUserRepository
{
private readonly IConnectionFactory _connectionFactory;
public UserRepository(IConnectionFactory connectionFactory) : base("User")
{
_connectionFactory = connectionFactory;
}
public async Task<User> Delete(User model)
{
var result = await Delete(model);
return result;
}
}
The thing is that I can't write entity.Id in my Delete-opration in my generic repository. I get a error. So how can I easily implement CRUD-operations like this?
Here is the error message:
TEntity does not contain a definition of "Id" and no extension method "Id" accepting a argument of type "TEntity" could be found
Define an interface like so.
public interface ITypeWithId {
int Id {get;}
}
And make sure your User type implements that interface.
Now apply it to your class as a generic constraint.
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class, ITypeWithId
If you have types that are stored in the repository but DO Not have an Id property then make your delete type constraint specific to the method and not the class. This will allow you to still use the same repository type even with types that might key on something else like a string or a compound (multi) key.
public void Delete<T>(T entity) where T : class, ITypeWithId
{
using (IDbConnection cn = Connection)
{
cn.Open();
cn.Execute("DELETE FROM " + _tableName + " WHERE Id=#ID", new { ID = entity.Id });
}
}
Please don't do this! Your generic repository adds more confusion than value. It's fragile code (string literals for _tableName, invalid cast errors on the id parameter), and introduces a gaping security hole (sql injection via _tableName). If you've chosen Dapper, it's because you want to be in control of your sql, so it makes no sense to generate the sql you send to Dapper.
you have to define an interface like below
public interface IIdentityEntity
{
public int Id { get; set;}
}
all your entities which want to use the class, must implement the IIdentityEntity.
and the first line should be changed to the following
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class,IIdentityEntity
and what was the problem is that you only described the TEntity as class and class does not have an Id in its description so you have to notify compiler that the Generic type implemented an Interface that holds an Id field inside it
In case it helps, I've just published a library Harbin.DataAccess which implements Generic Repositories (Generic Repository Pattern) using "raw" Dapper, Dapper.FastCRUD, and DapperQueryBuilder:
The Inserts/Updates/Deletes are automatically generated by Dapper FastCRUD (class should be decorated with attributes for keys/autoincrement columns)
Supports FastCRUD bulk update, bulk delete, and async methods.
Repositories can be extended with custom Queries and custom Commands (allows/promotes CQRS separation)
Queries can be defined manually (raw sql) or using Dapper FastCRUD syntax
Dynamic Queries (dynamic number of conditions) can be built using DapperQueryBuilder
There are Read-only Connection Wrappers and Read-only Repositories, so it's easy to use read-replicas (or multiple databases)
Support for ADO.NET transactions
Support for mocking Queries and Commands
Sample Insert/Update/Delete (Generic Repository - this uses Dapper FastCRUD):
var conn = new ReadWriteDbConnection(new System.Data.SqlClient.SqlConnection(connectionString));
// Get a IReadWriteRepository<TEntity> which offers some helpers to Query and Write our table:
var repo = conn.GetReadWriteRepository<ContactType>();
var contactType = repo.QueryAll().First();
// Updating a record
contactType.ModifiedDate = DateTime.Now;
repo.Update(contactType);
// Adding a new record
var newContactType = new ContactType() { Name = "NewType", ModifiedDate = DateTime.Now };
repo.Insert(newContactType);
// FastCRUD will automatically update the auto-generated columns back (identity or guid)
// Deleting a record
repo.Delete(newContactType);
[Table("ContactType", Schema = "Person")]
public class ContactType
{
[Key] // if column is part of primary key
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] // if column is auto-increment
public int ContactTypeId { get; set; }
public DateTime ModifiedDate { get; set; }
public string Name { get; set; }
}
Sample Dynamic Queries:
var conn = new ReadDbConnection(new System.Data.SqlClient.SqlConnection(connectionString));
// Get a IReadRepository<TEntity> which offers some helpers to Query our table:
var repo = conn.GetReadRepository<Person>();
// Custom Query (pure Dapper)
var people = repo.Query("SELECT * FROM Person.Person WHERE PersonType = #personType ", new { personType = "EM" } );
// DapperQueryBuilder allows to dynamically append conditions using string interpolation (but injection-safe)
string type = "EM"; string search = "%Sales%";
var dynamicQuery = repo.QueryBuilder(); // if not specified query is initialized with "SELECT * FROM tablename"
dynamicQuery.Where($"PersonType = {type}");
dynamicQuery.Where($"ModifiedDate >= {DateTime.Now.AddDays(-1)} ");
dynamicQuery.Where($"Name LIKE {search}");
// Result is SELECT * FROM [Person].[Person] WHERE PersonType = #p0 AND ModifiedDate >= #p1 AND Name LIKE #p2
var people = dynamicQuery.Query();
Extending Repositories (adding custom Queries and Commands) using Inheritance:
public class PersonRepository : ReadWriteDbRepository<Person>
{
public PersonRepository(IReadWriteDbConnection db) : base(db)
{
}
public virtual IEnumerable<Person> QueryRecentEmployees()
{
return this.Query("SELECT TOP 10 * FROM [Person].[Person] WHERE [PersonType]='EM' ORDER BY [ModifiedDate] DESC");
}
public virtual void UpdateCustomers()
{
this.Execute("UPDATE [Person].[Person] SET [FirstName]='Rick' WHERE [PersonType]='EM' ");
}
}
public void Sample()
{
// Registers that GetReadWriteRepository<Person>() should return a derived type PersonRepository
ReadWriteDbConnection.RegisterRepositoryType<Person, PersonRepository>();
var conn = new ReadWriteDbConnection(new System.Data.SqlClient.SqlConnection(connectionString));
// we know exactly what subtype to expect, so we can just cast.
var repo = (PersonRepository) conn.GetReadWriteRepository<Person>();
repo.UpdateCustomers();
var recentEmployees = repo.QueryRecentEmployees();
}
Full documentation here.

Best approach for view model creation to handle single entity and list - return IQuerable

Looking for input on the best approach/pattern to meet the following requirement for a view model class:
Converts an IQueryable from a repository select to a IQueryable view model query <-- Works fine
Converts a single instance of a db entity to a view model instance <-- is not working, returns NULL
Both use a single method to map db entity to view model properties to avoid mapping replication
Example of what I am attempting, but it is not working...and seems maybe a bit of a hack:
public class WorkOrderDependencyViewModel : IEntity, IViewModel<WorkOrderDependency, WorkOrderDependencyViewModel>
{
public int Id { get; set; } }
public int WorkOrderHeaderId { get; set; }
public int POHeaderId { get; set; }
public decimal RemainQty { get; set; }
//Re-use this mapping logic for both converting a query and converting a single db entity instance. Used by Kendo Grids
public IQueryable<WorkOrderDependencyViewModel> ConvertClassQueryToViewModelQuery(IQueryable<WorkOrderDependency> entityQuery)
{
var viewModelResultQuery = entityQuery
.Select(x => new WorkOrderDependencyViewModel()
{
Id = x.Id,
WorkOrderHeaderId = x.WorkOrderHeaderId,
POHeaderId = x.PODetail.POHeaderId,
RemainQty = x.PODetail.QtyOrdered - x.PODetail.QtyReceived
}
);
return viewModelResultQuery;
}
//convert single instance of db entity to view model, but use existing mapping logic from above method
public WorkOrderDependencyViewModel ConvertClassToViewModel(WorkOrderDependency entity)
{
var entityList = new List<WorkOrderDependency>();
entityList.Add(entity);
var viewModel = ConvertClassQueryToViewModelQuery(entityList.AsQueryable()).FirstOrDefault() as WorkOrderDependencyViewModel;
return viewModel; <------ viewModel is NULL
}
}
Why is viewModel returning NULL?
This would be a much shorter and easier way to do this if you don't need IQueryable
public WorkOrderDependencyViewModel ConvertClassToViewModel(
WorkOrderDependency entity)
{
return new WorkOrderDependencyViewModel
{
Id = entity.Id,
WorkOrderHeaderId = entity.WorkOrderHeaderId,
POHeaderId = entity.PODetail.POHeaderId,
RemainQty = entity.PODetail.QtyOrdered - entity.PODetail.QtyReceived
};
}
Edit
If you are using this as part of a linq query, maybe you can use Automapper or a func like this
private static readonly Expression<Func<WorkOrderDependency, WorkOrderDependencyViewModel>> AsViewModel =
entity => new WorkOrderDependencyViewModel
{
Id = entity.Id,
WorkOrderHeaderId = entity.WorkOrderHeaderId,
POHeaderId = entity.PODetail.POHeaderId,
RemainQty = entity.PODetail.QtyOrdered - entity.PODetail.QtyReceived
};
You would use it in your query like this
public IQueryable<WorkOrderDependencyViewModel> GetViewModel()
{
return repository.WorkOrderDependencies // change to suit your query needs
.Select(AsViewModel);
}

How to Return Extra Data with IQueryable method?

I am using Entity Framework and Breeze. For an Entity, there is a bit of associated data I would like to provide with the entity. Getting this data is most efficiently done by querying the Entity table and joining to other tables; this query includes a group by sub-query.
I am attempting to tack this extra data on by adding it as a [NotMapped] field to the entity:
[NotMapped]
public string NotMappedField { get; set; }
So then I basically want to replace this webapi controller method
[HttpGet]
public IQueryable<MyObject> MyObjects()
{
return _contextProvider.Context.MyObjects;
}
With something like this:
public IQueryable<MyObject> MyObjectsWithExtraData()
{
var query = from o in _contextProvider.Context.MyObjects
// big complex query
select new MyObject
{
FieldA = o.FieldA,
FieldB = o.FieldB,
// all fields
NotMappedField = x.ResultFromComplexJoin
}
return query;
}
This gives me an error:
The entity or complex type 'MyObject' cannot be constructed in a LINQ to Entities query.
I've tried this a few ways and it seems to fight me both from the EF side and the Breeze side. I need to keep this as returning something like IQueryable so I can filter from the client through webapi because doing something like a ToList() here causes memory issues due to the dataset size.
So my question is - is there a best practices kind of way to accomplish what I am attempting or can anyone provide a solution?
Update:
I have found you can return extra data alongside of your entity and still have access to the entity as a queryable from Breeze:
public object MyObjectsWithExtraData()
{
var query = from o in _contextProvider.Context.MyObjects
// big complex query....
select new
{
theObject = MyObject,
NotMappedField = x.ResultFromComplexJoin
};
return query;
}
and then from the client breeze side you can do something like this:
var query = breeze.EntityQuery
.from("MyObjectsWithExtraData")
.where("theObject.FieldA", "Equals", 1)
.expand("theObject.SomeNavigationalProperty")
.orderBy("theObject.FieldB");
Still not exactly what I was looking for but it is actually pretty slick.
Take a look at the EntityQuery.withParameters method.
// client side
var q = EntityQuery.from("CustomersStartingWith")
.withParameters({ companyName: "C" });
// server side
[HttpGet]
public IQueryable<Customer> CustomersStartingWith(string companyName) {
var custs = ContextProvider.Context.Customers.Where(c => c.CompanyName.StartsWith(companyName));
return custs;
}
You can also mix and match a combination of regular query predicates with these custom parameters.
LINQ to entity can only construct pur "Data Transfert Object" : class containing only public properties with trivial getter and setter and without constructor.
See my answer to a similar question here : https://stackoverflow.com/a/21174654/3187237
I specify my answer
An Entity class can't be instanciated in a LINQ to Entities query.
If you want to construct similar (or almost similar) in the query you have to define an other class.
In your case you want to return object almost similar to your MyObject. So you have to define a class:
public class MyObjectExtended
{
public string FieldA { get; set; }
public string FieldB { get; set; }
// ... all other MyObjetc fields
public string ExtraFieldA { get; set; }
public string ExtraFieldB { get; set; }
}
Now, your service can return a IQueryable<MyObjectExtended>:
public IQueryable<MyObjectExtended> MyObjectsWithExtraData() {
var myQuery = from o in _contextProvider.Context.MyObjects
// big complex query....
select new MyObjectExtended {
FieldA = o.FieldA,
FieldB = o.FieldB,
//... all fields ...
ExtraFieldA = x.ResultFromComplexJoinA,
ExtraFieldB = x.ResultFromComplexJoinB
};
return myQuery;
}
I hope this is what you are looking for.

Categories