Refactoring LINQ statement using Entity Framework to a method - c#

I have these Linq queries that I use multiple times in my code. I want to make a method of them and return their results.
var myevent = (from v in myEntities.Events
where _EventID == v.EventID
select v).SingleOrDefault();
var comments = (from c in myEntities.Fora
orderby c.DateCreated descending
where c.EventID == _EventID
select c).ToList();

Your question isn't very clear, but it sounds like you just want two methods to return the results of the linq query.
public Events GetEvent(int id)
{
return (from v in myEntities.Events
where _EventID == id
select v).SingleOrDefault();
}
public List<Fora> GetComments(int id)
{
return (from c in myEntities.Fora
orderby c.DateCreated descending
where c.EventID == id
select c).ToList();
}

Your question has too many unknowns to answer concretely, and this is something the SO is particular about.
I'm going to assume your entity is DB-backed.
You need to be aware of the transaction scope. You could use a separate class with a static "myEntities" and implement as Anonymous above. However if different threads are calling in you may end up with a nested transaction. You also don't want to create a new connection every time you call it.
I would implement this as part of a broader data access layer and pass your connection to it as part of the constructor. If you choose to use a static method then you should pass the connection as one of the params.
Or you could have an abstract class with this logic, and have child classes implement it.

Related

C# Linq Group by Object

I have an issue of using group by in LINQ to SQL statement.
The cod I have is
var combinedItems = (from article in articles
join author in authors
on article.AuthorId equals author.Id into tempAuthors
from tempAuthor in tempAuthors.DefaultIfEmpty()
select new { article , author = tempAuthor});
var groups1 = (from combinedItem in combinedItems
group combinedItem by combinedItem.article into g
select g.Key).ToList();
var groups2 = (from combinedItem in combinedItems
group combinedItem by combinedItem.article.Id into g
select g.Key).ToList();
I tried to group in two different ways. The first way, I group by an object and the second way I just group by a field in one of the objects.
When I run groups1, I got an error saying need to evaluate in client side, while when I use groups2, it works all good. Can I ask what could be wrong? If I want to group by object, is there any way to do it?
In case you want to group by object, as you've not overridden Equals and GetHashCode() in your Article class or implemented IEqualityComparer<Article> you're just getting the default comparison, which checks if the references are equal. So what you need is something like this:
class GroupItemComparer : IEqualityComparer<Article>
{
public bool Equals(Article x, Article y)
{
return x.Id == y.Id &&
x.Name == y.Name;
}
public int GetHashCode(Article obj)
{
return obj.Id.GetHashCode() ^
obj.Name.GetHashCode();
}
}
And then you need to change your query to lambda expression:
var groups1 = combinedItems.GroupBy(c => c.article , new GroupItemComparer())
.Select(c => c.Key).ToList();
In case you got any exception regarding translation your method to SQL, you can use AsEnumerable or ToList methods before your GroupBy method, with this methods after data is loaded, any further operation is performed using Linq to Objects, on the data already in memory.
As others have pointed out, the GroupBy is using reference equality by default, and you could get around it by specifying one or more properties to group by. But why is that an error?
The whole point of the query is to translate your Linq query into SQL. Since object reference equality on the client can't be easily translated to SQL, the translator doesn't support it and gives you an error.
When you provide one or more properties to group by, the provider can translate that to SQL (e.g. GROUP BY article.Id), and thus the second method works without error.

How to implement a generic method in Repository to make joins in linq

Hello I have a linq query that makes several joins on tables and which eventually returns me a single row.
For the time being, I have used the generic method GetAll() that returns the single row.
The query that I have written is as follows:
from h in _repositoryWorkers.GetAll()
join p in _repositoryJobs.GetAll() on h.Id equals p.THI_N_ID
join q in _repositoryHome.GetAll() on h.Id equals q.THI_N_ID
public Repository()
{
this.context = new WorkersContext();
entities = context.Set<T>();
}
public IEnumerable<T> GetAll()
{
return entities.AsEnumerable<T>();
}
What I want to achieve is the following as an example:
from w in repo.Query<Worker>()
join e in repo.Query<XEntity>() on ...
I do not want to use a List that returns me only a single row. I just want to return an object of this row.
Please advise.
In isolation, I would write something like:
var result = context.Worker()
.Include(worker => worker.XEntity)
.Where(worker => worker.ID == id)
.SingleOrDefault();
result will either be the single Worker entity with XEntity populated or null if there is no entry with a matching id.
How do you make it generic though?
I've tried a lot of things over the last few years and in the end, I find that specific methods work best.
So I'll have a generic base repository for the simple stuff but then I'll extend that when I need to do things like this.
So I would have a specific repository for the Worker entity that extends the generic base.
It would have a method like:
public Worker GetByIdWithXEntity(id)
{
this.Queryable()
.Include(worker => worker.XEntity)
.Where(worker => worker.ID == id)
.SingleOrDefault();
}
(Queryable is a protected method on the base repository)
This approach is a bit of a pita with large projects but I've found that it works very nicely as it keeps all the data access logic in the Repo, it makes unit testing your services simple and it's very clear exactly what's going on.
That said, if anyone has a better way, I'd love to hear it!

I want to retrieve data from multiple tables to service payer into my project

want to retrieve data from multiple tables to service layer as I have to write database logic into service layer into my project
'KeysPlus.Service.Models.QuoteModel' to 'System.Collections.Generic.IEnumerable'.
An explicit conversion exists (are you missing a cast?)
Any help is highly appreciated!
public static IEnumerable<QuoteModel> GetJobQuotes(Login login, QuoteModel model)
{
using (var db = new KeysEntities())
{
var quotes = db.JobQuote.Where( x=> x.ProviderId == login.Id);
model = (from q in db.JobQuote
join j in db.Job on q.JobId equals j.Id
join p in db.Property on j.PropertyId equals p.Id
join a in db.Address on p.AddressId equals a.AddressId
select new QuoteModel
{
JobDescription=j.JobDescription,
QuoteAmount=q.Amount,
PropertyAddress= a.City
}).ToList();
return model;
}
}
You have a parameter in your method named model which is a single QuoteModel and you trying to set its value to a collection of QuoteModel. Remove that parameter (you not using it) and change the code to
using (var db = new KeysEntities())
{
var quotes = db.JobQuote.Where( x=> x.ProviderId == login.Id);
IEnumerable<QuoteModel> model = (from q in db.JobQuote
....
}
You have a model instance:
QuoteModel model
And you're trying to set it to a collection of model instances:
model = (from q in db.JobQuote
...
).ToList();
An apple is not a basket of apples. So you need to do one of two things, depending on what you actually want this method to do...
Return one model?
If you're looking for a specific instance from that list, then you need to add a clause to the query to return just that instance. Methods like .First() or .FirstOrDefault() can do that (or the .Single() equivalents). For example, if you want an instance which matches a specific value, it might look something like this:
model = (from q in db.JobQuote
...
).First(j => j.ID == someIdentifier);
Where of course someIdentifier is some value you have which you can use in the query. Perhaps model.ID or something like that? Depends on how you want to query your data, which we don't know here. But the point is that you need to return one model, not many models.
Additionally, you'd need to change the method's return type to QuoteModel, since you'd just be returning that one model instance.
Return many models?
Conversely, if you do want to return multiple models, then you can't put them in a variable which holds only one model. Just return the value directly:
return (from q in db.JobQuote
...
).ToList();
(Of course, then you're not even using the model variable, so why require it?)

Get a distinct list

I want to select a distinct list.
The following code is not working:
public IQueryable<BusinessObjects.Order> GetByBusinessId(Guid Id)
{
rentalEntities db = DataContextFactory.CreateContext();
List<Rental.BusinessObjects.Order> transformedList = new List<BusinessObjects.Order>();
foreach (Rental.DataObjects.EntityModel.Order item in db.Orders.Where(x => x.BusinessID == BusinessId).ToList())
{
transformedList.Add(OrderMappers.ToBusinessObject(item));
}
return( transformedList.AsQueryable()).Distinct();
}
Try this:
return Rental.DataObjects.EntityModel.Order item in db.Orders
.Where(x => x.BusinessID == BusinessId)
.Distinct()
.Select(item => OrderMappers.ToBusinessObject(item));
This should move the distinct operation to the underlying database call as it's applied before the query is materialized - this is more efficient as the duplicate rows aren't retrieved from the database server. If for some reason you don't want to do that, then check your equals implementation (as mentioned by Sorin)
You may want to check how your business objects implement Equals(), my guess is they are are different even if they have (let's say) the same ID.
You might like to try the DistinctBy() extension method from the MoreLinq library. This lets you easily control the exact semantics of how two objects are compared for distinctness. For instance:
return transformedList.AsQueryable().DistinctBy(orderBO => orderBO.OrderId);
http://morelinq.googlecode.com/files/morelinq-1.0-beta.zip

Return IQueryable containing 2 Subclasses

Can you return IQueryable which is composed of two or more different subclasses ? Here's my attempt to show what I mean. It throws the error:
System.NotSupportedException: Types in
Union or Concat have members assigned
in different order..
var a = from oi in db.OrderItems
where oi.OrderID == ID
&& oi.ListingID != null
select new OrderItemA {
// etc
} as OrderItem;
var b = from oi in db.OrderItems
where oi.OrderID == ID
&& oi.AdID != null
select new OrderItemB {
//etc
} as OrderItem;
return a.Concat<OrderItem>(b);
Try doing the concat on IEnumerable instead of IQueryable:
return a.AsEnumerable().Concat(b.AsEnumerable());
If you need an IQueryable result you could do this:
return a.AsEnumerable().Concat(b.AsEnumerable()).AsQueryable();
Doing this will force the concat to happen in-memory instead of in SQL, and any additional operations will also happen in-memory (LINQ To Objects).
However, unlike the .ToList() example, the execution should still be deferred (making your data lazy loaded).
My guess is that this is because you are using LINQ in an LINQ-to-SQL context.
So using Concat means that LINQ2SQL will need to join both query into a SQL UNION query which might be where the System.NotSupportedException originated from.
Can you try this:
return a.ToList().Concat<OrderItem>(b.ToList());
And see if it make any difference?
What the above does is that it executes the query twice and then concatenate them in-memory instead of hot-off-SQL as to avoid the query translation problem.
It might not be the ideal solution, but if this work, my assumption is probably correct, that it's a query translation problem:
More information about Union and Concat translation to SQL:
http://blog.benhall.me.uk/2007/08/linq-to-sql-difference-between-concat.html
http://msdn.microsoft.com/en-us/library/bb399342.aspx
http://msdn.microsoft.com/en-us/library/bb386979.aspx
Hope this helps.
Interestingly after reading your post and a bit of testing, I realized that what your actually doing does seem to work just fine for me given that the projection part you show as ellipsis in both of your queries match. You see, LINQ to SQL appears to construct the underlying projection for the SQL select command based off of the property assignment statements as opposed to the actual type being materialized so as long as both sides have the same number, type, and order (not sure about this) of member assignments the UNION query should be valid.
My solution that I've been working with is to create a property on my DataContext class which acts much like a SQL View in that it allows me to write a query (in my case a Union between two different tables) and then use that query as if it is itself like a table when composing read-only select statements.
public partial class MyDataContext
{
public IQueryable<MyView> MyView
{
get
{
var query1 =
from a in TableA
let default_ColumnFromB = (string)null
select new MyView()
{
ColumnFromA = a.ColumnFromA,
ColumnFromB = default_ColumnFromB,
ColumnSharedByAAndB = a.ColumnSharedByAAndB,
};
var query2 =
from a in TableB
let default_ColumnFromA = (decimal?)null
select new MyView()
{
ColumnFromA = default_ColumnFromA,
ColumnFromB = b.ColumnFromB,
ColumnSharedByAAndB = b.ColumnSharedByAAndB,
};
return query1.Union(query2);
}
}
}
public class MyView
{
public decimal? ColumnFromA { get; set; }
public string ColumnFromB { get; set; }
public int ColumnSharedByAAndB { get; set; }
}
Notice two key things:
First of all the projection formed by the queries which make up both halves of the Union have the same number, type, and order of columns. Now LINQ may require the order to be the same (not sure about this) but it is definitely true that SQL does for a UNION and we can be sure that LINQ will require at least the same type and number of columns and these "columns" are known by the member assignments and not from the properties of the type you are instantiating in your projection.
Secondly LINQ currently doesn't allow for multiple constants to be used within a projections for queries which formulate a Concat or Union and from my understanding this is mainly because these two separate queries are separately optimized before the Union operation is processed. Normally LINQ to SQL is smart enough to realize that if you have a constant value which is only being used in the projection, then why send it to SQL just to have it come right back the way it was instead of tacking it on as a post process after the raw data comes back from SQL Server. Unfortunately the problem here is that this is a case of LINQ to SQL being to smart for it's own good, as it optimizes each individual query too early in the process. The way I've found to work around this is to use the let keyword to form a range variable for each value in the projection which will be materialized by getting it's value from a constant. Somehow this tricks LINQ to SQL into carrying these constants through to the actual SQL command which keeps all expected columns in the resulting UNION. More on this technique can be found here.
Using this techinque I at least have something reusable so that no matter how complex or ugly the actual Union can get, especially with the range variables, that in your end queries you can write queries to these pseudo views such as MyView and deal with the complexity underneath.
Can you do the projection after the concat?
// construct the query
var a = from oi in db.OrderItems
where oi.OrderID == ID
&& oi.ListingID != null
select new {
type = "A"
item = oi
}
var b = from oi in db.OrderItems
where oi.OrderID == ID
&& oi.AdID != null
select new {
type = "B"
item = oi
}
var temp = a.Concat<OrderItem>(b);
// create concrete types after concatenation
// to avoid inheritance issue
var result = from oi in temp
select (oi.type == "A"
? (new OrderItemA {
// OrderItemA projection
} as OrderItem)
: (new OrderItemB {
// OrderItemB projection
} as OrderItem)
);
return result
Not sure if the ternary operator works in LINQ2SQL in the above scenario but that might help avoid the inheritance issue.

Categories