Error in generic repository method for Entity Framework - c#

I'm trying to create a generic method to use in my base class for my repositories and I'm having an issue. Here's the method...
public virtual T First(System.Linq.Expressions.Expression<Func<T, bool>> where, List<string> properties)
{
IQueryable<T> query = null;
if (where != null)
{
query = _context.CreateQuery<T>(String.Format("[{0}]", typeof(T).Name.ToString())).Where(where);
}
else
{
query = _context.CreateQuery<T>(String.Format("[{0}]", typeof(T).Name.ToString()));
}
foreach (string s in properties)
{
query = query.Include(s);
}
T _result = (T)query.First();
return _result;
}
When I run the code it gives me this error:
'Company' could not be resolved in
the current scope or context. Make
sure that all referenced variables are
in scope, that required schemas are
loaded, and that namespaces are
referenced correctly. Near escaped
identifier, line 1, column 1.
I have an idea on why it's doing this, I just don't know how to fix it. I think it's doing it because my ObjectContext doesn't know about the object "Company" but it does know "Companies". Any ideas on how to fix this??
The error happens on this line:
T _result = (T)query.First();
Thanks!

Dan, get the entity set name with something like the following:
string entitySetName = context.MetadataWorkspace
.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace)
.BaseEntitySets.Where(bes => bes.ElementType.Name == typeof(T).Name).First().Name;
string name = String.Format("{0}.{1}", context.DefaultContainerName, entitySetName);
query = context.CreateQuery<T>(name).Where(where);
Doing this will resolve the proper plural name for the query.
Using Yury's method would be the best option though.
EDIT By the way, you should return FirstOrDefault() instead of First() in case the query returns no entities (it will throw an InvalidOperationException).

Try to use
query = _context.CreateObjectSet<T>().Where(where);
instead of
query = _context.CreateQuery<T>(String.Format("[{0}]", typeof(T).Name.ToString())).Where(where);

Related

How to get a list of object from sql connection query?

I have sql query written in c#.(DataType class has str_name and int_something). Code is like the following
List<DataType> allDataType=SQLConnection.Query<DataType>("select str_name,int_something from sqlite_master where type = ?", type_name);
I already got parameterless constructor inside DataType, but still I got a list of object that hasn't been initialized. str_name is empty string while int_something is always 0. But I can get the correct number of rows of data. Any hint?
What actually SQLConnection.Query<T>(string s) is? SQLConnection doesn't have such a method, so I assume it's an extension method that you have defined in your project. The problem is that method. Try searching a bug in the implementation of that method.
This is what you looking for I gather
public List<NumberPattern> GetNumberPattern(int number)
{
using var cnn = new System.Data.SqlClient.SqlConnection(Server.ConnectionString);
var resp = cnn.Query<NumberPattern>($"exec GetNumberPatternRows {number};");
if (resp != null)
{
return resp.ToList();
}
return null;
}

Linq to Entities 4.0 ('Include'): In search of an elegant way

I'm learning Linq to entities on my own and I have a bit of a question. Take a look at the following method. It returns the latest completed task for every item inside the List of WorkflowsInProgress . This method is being called within an other method where i'm using the fields of TasksInProgress , but i'm also using properties of the linked Tasks and WorkflowInProgress. As you will see I've added the Include to the query.
public List<TasksInProgress> GetLatestTaskForWorkflowsInProcess(List<WorkflowInProgress> pWorkflowsInProcess)
{
List<TasksInProgress> tasksLst = new List<TasksInProgress>();
List<Int64> workflowIds = pWorkflowsInProcess.Select(w => w.Id).ToList();
using (TaskWorkflowEntities myDatacontext = new TaskWorkflowEntities())
{
tasksLst = (from taskP in myDatacontext.TasksInProgress.Include("Tasks").Include("WorkflowInProgress")
where (taskP.RunningState == (int)WorkflowRunningStates.Completed) &&
workflowIds.Contains(taskP.WorkflowInProgressId) &&
taskP.Completed == (from sTaskP in myDatacontext.TasksInProgress
where sTaskP.WorkflowInProgressId == taskP.WorkflowInProgressId
group sTaskP by sTaskP.WorkflowInProgressId into gSTaskP
select gSTaskP.Max(g => g.Completed)).FirstOrDefault()
select taskP).ToList<TasksInProgress>();
}
return tasksLst;
}
My Question is:
'Is there a more elegant way to include other tables inside a Query?'
Because i don't like those hardcoded objectnames just sitting there' (Imagine if the tablename changes...)
Or is there any other way that i can use to include the fields of linked objects/navigational properties?
Note: Example of the methode above this one:
foreach(TasksInProgress taskInProc in _taskWorkflowS.GetLatestTaskForWorkflowsInProcess(currentWorkflowsInProcess))
{
//Do something with (int)taskInProc.Tasks.TaskOrder
//Do something with taskInProc.WorkflowInProgress.WorkflowId
// ...
//for Instance
int i = 0;
i = _taskWorkflowS.GetAmountOfTasksForWorkflow(taskInProc.WorkflowInProgress.WorkflowId, (int)taskInProc.Tasks.TaskOrder)
if (i > 0 )
{ ... }
}
Update:
using lambda expression as parameter of the Include doesn't appear to be working due to the fact that it only excepts string (see image below):
Edit: Answer before the question was changed to Entity Framework 4.0. This will only work for EF 4.1 and later
You can have
.Include(o => o.Tasks)
if you add
using System.Data.Entity;
at least you are not using strings then and you will get errors if the table changes
You can write an extension method that use a lambda expression instead of a string:
public static ObjectQuery<T> Include<T>(this ObjectQuery<T> query, Expression<Func<T, object>> selector)
{
MemberExpression body = selector.Body as MemberExpression;
return query.Include(body.Member.Name);
}
and use it like:
myDatacontext.TasksInProgress.Include(q=>q.Tasks)
.Include(q=>q.WorkflowInProgress)
I have not tested it, but it should work.

Why is Linq-to-Entities recognizing query in query, but not when query is returned from a method?

This is a common error I've found, but I'm not sure how to fix it.
LINQ to Entities does not recognize the method
'System.Linq.IQueryable`1[ProjectX.Models.DTOs.ExampleDTOUDC] Select(Int32)'
method, and this method cannot be translated into a store expression.
Here is the offending code in question.
public IQueryable<ExampleDTO> Select()
{
return from e in db.vw_Example
select new ExampleDTO()
{
exampleUDCs = new ExampleUDCRepository().Select(v.VehicleID).AsEnumerable()
//etc
};
}
and the ExampleUDCRepository.Select() method is:
public IQueryable<ExampleUDCDTO> Select(int id)
{
return from eudc in db.ExampleUDCs
where eudc.ExampleID == id
select new ExampleUDCDTO()
{
ExampleID = eudc.ExampleID,
//etc
};
}
I understand why Linq needs everything to be in a form it can be turned into an SQL query, but what I don't understand is why this works I take that same query from the first Select() method and do something like:
public IQueryable<ExampleDTO> Select()
{
return from e in db.vw_Example
select new ExampleDTO()
{
ExampleID = e.ExampleID,
ExampleUDCs = (from eudc in db.ExampleUDCs
where eudc.ExampleID == id
select new ExampleUDCDTO()
{
ExampleID = eudc.ExampleID
//etc
})
};
}
In case you wanted to see it, here is the ExampleUDCs declaration in my ExampleDTO class:
public IEnumerable<ExampleUDCDTO> ExampleUDCs { get; set; }
Does anyone know a way for me to fix this to reuse code from my ExampleUDCDTO().Select(int id) method instead of putting all of the queries into the first Select() method?
Please let me know if you need any more information. Thank you.
Entity Framework works with expressions, and it tries to compile the complete expression into a piece of SQL. Since you are creating an expression with a method call in it, EF tries to compile that method call into SQL as well. And that is not possible.

Getting name of specific linq to sql table

I am using Linq to SQL to manipulate and MS Access database.
To speed up batch modifications I found it more efficient to directly execute queries using the datacontext, like so context.ExecutCommand("DELETE FROM [MyTable];"). For the sake of efficiency I'd like to make this an extension method, but I don't know how to retrieve the table name from the context...
I know I could just pass the table name as a hardcoded string, something like:
public static void DeleteAll(this Table<TEntity> MyTable)
{
string tableName = // retrieve MyTable's name
MyTable.Context.ExecuteCommand(string.Format("DELETE FROM [{0}];", tableName));
}
I got some way towards getting the table name, but need some help to get thsi working. So far I have:
var tableName = dc.MyTables.Context.GetTable(typeof(MyTable)).ElementType.Name;
But can't figure out how to retrieve the type of the entities in MyTables so as not have to hardcode the argument of .GetTable() and make this usable for any table I pass in.
Any answer in C# or VB is fine. Thanks.
EDIT
To summarise what I am looking for is a way to get the entity type of a table, from the table itself. Something like Context.MyTable.GetEntityType()... if only it were that easy.
I am not sure if this works for EF, but I use this approach in Linq to Sql.
You'll have to use attributes from System.Data.Linq.Mapping namespace. If you open the *.designer.cs file, containing the definition of any Linq to Sql entity, you'll find a line like this above the declaration of the class:
[global::System.Data.Linq.Mapping.TableAttribute(Name="YourTableName")]
So each entity class in Linq to Sql is marked with the TableAttribute attribute and it's Name property contains the name you need. We may use this:
public static string GetTableName<TEntity>(this Table<TEntity> MyTable)
where TEntity : class
{
Type type = typeof(TEntity);
object[] temp = type.GetCustomAttributes(
typeof(System.Data.Linq.Mapping.TableAttribute),
true);
if (temp.Length == 0)
return null;
else
return (temp[0] as System.Data.Linq.Mapping.TableAttribute).Name;
}
This should also work:
MyTable.Context.Mapping.GetTable(typeof(TEntity)).TableName
Since I do not have attributes on my tables, I need to get the entity name, which is what Linq to SQL will use in this case, so based on Konstantin Vasilicov's answer, the extension method becomes:
public static string GetTableName<TEntity>(this Table<TEntity> MyTable) where TEntity : class
{
string name = string.Empty;
Type type;
object[] attributes;
type = typeof(TEntity);
attributes = type.GetCustomAttributes(typeof(TableAttribute), true);
if (attributes.Length > 0)
name = ((TableAttribute)attributes[0]).Name;
if (!string.IsNullOrEmpty(name))
return name;
return type.Name;
}

"Member access 'DataType MemberName' of 'Namespace.ClassName' not legal on type 'System.Collections.Generic.IEnumerable`1[Namespace.ClassName]."

I would love a solution to my current problem, but I would love EVEN MORE if I can understand what that error actually means.
I have LINQ to SQL classes for two tables in my DB: Providers and Assignments. I added the following member to the Provider class:
public IEnumerable<Assignment> Assignments
{
get
{
return (new linqDataContext())
.Assignments
.Where(a => a.ProviderID == this.ProviderID);
}
}
Then, I bind a GridView using a query that pulls from the parent Provider and uses the child member Assignments, like this:
protected void PopulateProviders()
{
linqDataContext context = new linqDataContext();
var list = from p in context.Providers
where (p.Assignments.Count(a => a.Category == ddlCategory.SelectedValue) > 0)
select p;
lvProviders.DataSource = list;
lvProviders.DataBind();
}
At .DataBind(), when it actually runs the query, it throws the following error:
Member access 'System.String Category' of 'Namespace.Assignment' not legal on type 'System.Collections.Generic.IEnumerable`1[Namespace.Assignment].
I've tried checking for nulls (a => a != null && a.Category ...) but that hasn't worked. I'm not sure what to try next. I think that, if I knew what the error is trying to tell me, I could find the solution. As it stands, I don't know why member access would be illegal.
That Assignments property is all wrong. First of all, property getters should not have side-effects, and more importantly, entity classes should never have reverse dependencies on the DataContext. Linq to SQL has no way to decipher this query; it's relying on a property that does all sorts of crazy stuff that Linq to SQL can't hope to understand.
Get rid of that Assignments property now. Instead of doing that, you need to write this query as a join:
int category = (int)ddlCategory.SelectedValue;
var providers =
from p in context.Providers
join a in context.Assignments
on p.ProviderID equals a.ProviderID
into g
where g.Count(ga => ga.Category == category) > 0
select p;
That should do what you're trying to do if I understood the intent of your code correctly.
One last side note: You never dispose properly of the DataContext in any of your methods. You should wrap it like so:
using (var context = new linqDataContext())
{
// Get the data here
}
I think somewhere it doesn't know that type that is in the IEnumerable. You are trying to call a method that is not part of the IEnumerable inteface.
Why don't you just move the query from the property out to the PopulateProviders() method?
Remove your custom-defined Assignments property. In the your Linq-To-SQL dbml file, create an association between Providers and Assignments, with Providers as the parent property, and ProviderID as the Participating Property for both entities. LINQ will generate a property "IEnumerable Assignments" based on matches between ProviderID using a consistent DataContext.

Categories

Resources