I have an Entity set that has Entities with a compound key containing ID (GUID) and CreatedAt (DateTime). This CreatedAt is when the entity was created. Each record represents each version of each entity such that muliple records can have the same ID.
I want to create an IQueryable-returning method that I can re-use such that it will only return the latest version of the entity requested, but I'm struggling to find the answer (if, indeed, there is an answer).
I know that I can write a query such as
(from e in context.Entities where e.ID = myID orderby e.CreatedAt).FirstOrDefault();
But I want to be able to do this instead:
(from e in context.GetCurrentEntities() where e.ID = myID).FirstOrDefault();
Such that it will only return the latest versions of the entity required.
Is this doable?
Many thanks for your help.
Lee
If you group by ID, you can select only the most recent from each group using something like this:
public IQueryable<Entity> GetCurrentEntities()
{
return from e in this.Entities
group e by e.ID into g
select g.OrderByDescending(e => e.CreatedAt).First();
}
You don't need FirstOrDefault() because a group won't be created unless there's at least one Entity in the group.
How about:
public partial class MyEntities // or whatever you call your ObjectContext
{
public IQueryable<Entity> GetCurrentEntities()
{
return from e in context.Entities
group e by e.ID into g
from ge in g
orderby ge.CreatedAt desc
select ge.First();
}
}
This is off the top of my head, but it should get you started.
I think you are looking at a smaller picture. Have you considered implementing repository pattern?LINK otherwise looking at your requirements you will need to add this GetCurrentEntities method to your context class.
Related
We are using CRM 2011. We have Contracts with Entity Reference to Product and each Product has an Entity Reference to a Subject. Given a Contract Guid, I need to retrieve the Subject Guid.
I am a beginner at LINQ but I coded:
var subject = from s in context.SubjectSet
join product in context.ProductSet
on s.Id equals product.SubjectId.Id
join contract in context.ContractSet
on product.Id equals contract.ce_ProductId.Id
where contract.Id == gContractId
select s;
foreach (var s in subject)
{
newReportableAction.ce_SupergroupRegarding =
new EntityReference(Xrm.Subject.EntityLogicalName, new Guid(s.Id.ToString()));
}
This throws an error:
AttributeFrom and AttributeTo must be either both specified or both ommited. You can not pass only one or the other. AttributeFrom: , AttributeTo: ce_ProductId
What does this error mean?
How can I get the Guid?
Update:
I tried breaking the query into parts to see where the error was being generated from so I had:
var query = from product in context.ProductSet
join contract in context.ContractSet
on product.Id equals contract.ce_ProductId.Id
This gives:
"The type of one of expressions in the join clause is incorrect. Type inference failed in the call to 'Join'"
Thank you to all who help...
I don't think you can use the .Id property right off the entity in Linq statements. Try this instead:
var query = from product in context.ProductSet
join contract in context.ContractSet
on product.ProductId equals contract.ce_ProductId.Id
Notice product.ProductId instead of product.Id.
So we copy and paste the exact same query from LinqPad into our EF 4.3 application, pointed at the exact same database and get a different result. In LinqPad we get 2 records returned. In our application we reaise an error "Object reference not set to an instance of an object."
var Shippings = shippingRepository.All.ToArray();
var SalesOrderHeaders = salesOrderHeaderRepository.All.ToArray();
var Customers = customerRepository.All.ToArray();
var Stores = storeRepository.All.ToArray();
var Departments = departmentRepository.All.ToArray();
var toShip = from sh in Shippings
join h in SalesOrderHeaders on sh.OrderId equals h.SalesOrderHeaderId
join c in Customers on h.CustomerId equals c.CustomerId
join st in Stores on h.StoreId equals st.StoreId
join d in Departments on h.DepartmentId equals d.DepartmentId into outer
from o in outer.DefaultIfEmpty()
select new
{
OrderId = sh.OrderId,
CustomerName = c.Name,
StoreName = st.Name,
DepartmentName = (o.Name == null) ? o.Name : "None",
DeliveryDate = h.DeliveryDateTime
};
In the application code, when we remove the outer join (to add Departments) and it's associated field the query returns the same 2 records asn in LinqPad.
Does anyone have any insight into how to fix this feature?
Click on "Add a connection" in linqpad and select datacontext from assembly like
You can choose Entity Framework datacontext or Entity Framework BDContext with POCO depending upon your scenario. click next and provide path to the assembly along with connection string and you will be good to go.
In LINQPad are you actually querying against your entity model? Take a look at this link if you aren't. I had a similar problem when starting out and didn't realize I had set up a default LINQ to SQL connection earlier and was querying against that.
I'm having issues with GROUP BY and eager loading. I try to explain what im doing.
I'm querying a datacontext ctx for events
The event class has the following properties
string Description
DateTime Date
bool IsDeleted
Guid SubjectId
string Title
DateTime Created
Guid ForProjectId
Person TriggeredBy
List<Guid> Targets
There are muttiple events with the same SubjectId and i would like to end up having events with unique SubjectIds and that are the newest in the group. I end up with the following query.
var events = from x in
(from e in ctx.Events
.Include("TriggeredBy")
.Include("Targets")
group e by e.SubjectId
into g
select new
{
GroupId = g.Key,
EventsWithSameSubjectId = g,
}
)
select x.EventsWithSameSubjectId
.OrderByDescending(y => y.Created).FirstOrDefault();
The query compile fine and returns the right resulting set. But the included properties are always null.
When i strip the query to see if eagor loading is working properly....
var events = (from e in ctx.Events.OfType<DataNotificationEvent>()
.Include("TriggeredBy")
.Include("Targets")
select e).ToList();
This return the events with all the included properties.
Is this a known issue / bug with Linq / EF or is there any way i can get rid of this error.
Regards
Vincent Ottens
You're projecting onto an anonymous type, so Include() isn't going to work like that. Because what you've done with the group and projecting into the anonymous type is to change the shape of the query. That tosses out the eager loading. Reading this article might help.
Thnx for the quick answer. You pointed me in the right direction. Here is the solution i came up with:
using MyFunc = Func<ExtendedCoreContext, Guid, IQueryable<DataNotificationEvent>>;
private static readonly MyFunc GetMentionsNewCompiledQuery =
CompiledQuery.Compile<ExtendedCoreContext, Guid, IQueryable<DataNotificationEvent>>(
(ctx, personId) => ((ObjectQuery<DataNotificationEvent>)(
from x in (
from e in ctx.Events.OfType<DataNotificationEvent>()
group e by e.SubjectId
into g
select g.OrderByDescending(p => p.Created).FirstOrDefault()
)
orderby x.Created descending
where x.Targets.Any(t => t.Id == personId)
select x
))
.Include(EntityProperties.Event.TriggeredBy)
.Include(EntityProperties.DataNotificationEvent.Targets)
);
I have this sql that i want to have written in linq extension method returning an entity from my edm:
SELECT p.[Id],p.[Firstname],p.[Lastname],prt.[AddressId],prt.[Street],prt.[City]
FROM [Person] p
CROSS APPLY (
SELECT TOP(1) pa.[AddressId],a.[ValidFrom],a.[Street],a.[City]
FROM [Person_Addresses] pa
LEFT OUTER JOIN [Addresses] AS a
ON a.[Id] = pa.[AddressId]
WHERE p.[Id] = pa.[PersonId]
ORDER BY a.[ValidFrom] DESC ) prt
Also could this be re-written in linq extension method using 3 joins?
Assuming you have set the Person_Addresses table up as a pure relation table (i.e., with no data besides the foreign keys) this should do the trick:
var persons = model.People
.Select(p => new { p = p, a = p.Addresses.OrderByDescending(a=>a.ValidFrom).First() })
.Select(p => new { p.p.Id, p.p.Firstname, p.p.LastName, AddressId = p.a.Id, p.a.Street, p.a.City });
The first Select() orders the addresses and picks the latest one, and the second one returns an anonymous type with the properties specified in your query.
If you have more data in your relation table you're gonna have to use joins but this way you're free from them. In my opinion, this is more easy to read.
NOTE: You might get an exception if any entry in Persons have no addresses connected to them, although I haven't tried it out.
I have an entity object (Company) which has 1 or more subobjects (CompanyRevision) represented as a non-null FK relationship in the database.
Using LINQ, I want to get all the Companies from the database, but I also only want the latest CompanyRevision for each company.
This is how I do it today, but I have a feeling this could be done using one query.
IEnumerable<Company> companyList = from p in ctx.Company.Include("CompanyRevisions")
select p;
foreach(Company c in companyList)
{
CompanyRevision cr = (from p in c.CompanyRevisions
orderby p.Timestamp descending
select p).First();
// Do something with c and cr...
}
As you can see, I would like to add this second LINQ query (the one that gets the latest CompanyRevision) into the first one, so that companyList[i].CompanyRevisions is basicly a list with just one entry (the latest one). I can't for the life of my figure out how to do this. Please help!
Thanks in advance
how about this: mixing the linq language and extension methods:
var results = from p in ctx.Company.Include("CompanyRevisions")
select new {Company = p,
Revision = p.CompanyRevisions.OrderByDescending(cr => cr.Timestamp).First()
}
Each result now has a Company and Revision member.
It's possible that you could also do this -
var results = from p in ctx.Company.Include("CompanyRevisions")
select new {Company = p,
Revision = (from pcr in p.CompanyRevisions
orderby pcr.Timestamp descending
select pcr).First()
}
To give the same results.
Although that's a guess - I haven't labbed that one out; but it's how I would try it first.