1

I'm trying to run this simple query:

var appt = (from a in context.AppointmentSet
           select new Appointment{ ModifiedOn = a.ModifiedOn}).First();

but I'm getting a compiler exception since ModifiedOn is readonly.

  • I could just return a, but then all the attributes of the Appointment entity will be returned, not just the ModifiedOn.
  • I could return new { a.ModifiedOn }, but then appt would be an AnonymousType and not an Appointment.

What's the suggested way to make this work?

Note, this is an example, assume that I'm returning more than just a single property from Appointment, and then there is a where criteria of some sort

Daryl
  • 18,592
  • 9
  • 78
  • 145
  • sounds like you need to call the constructor. – Hogan Dec 23 '14 at 15:43
  • There is only one constructor for Appointment and it's empty. (CRM has a very minimal implementation of LINQ) – Daryl Dec 23 '14 at 15:44
  • oh I see. Yes if you want to return an Appointment you have to return the whole thing just select a. – Hogan Dec 23 '14 at 15:45
  • @Hogan, but if I just select a, in CRM land it is expensive, since it will return all columns of the Appointment. – Daryl Dec 23 '14 at 15:48
  • yes... this is the design choice you have to make -- return a new type or return an existing type that is bigger. – Hogan Dec 23 '14 at 15:49
  • 1
    Why are you assigning ModifiedOn? It is read-only and controlled by CRM (hence your error). I'm not sure what you are actually trying to accomplish here... – Jason Faulkner Dec 23 '14 at 16:06
  • 1
    @JasonFaulkner, I'm trying to perform a Linq to CRM query, where I am retrieving a subset of attributes of an Appointment, but still retrieve an appointment, and not an anonymous type. – Daryl Dec 23 '14 at 16:13
  • Well I just gave it a shot - but I could only get to it using anonymous types (which I know you don't want). Unfortunately, system fields cannot be retrieved via your preferred method. – Jason Faulkner Dec 23 '14 at 16:34
  • @JasonFaulkner... what the crap just happened? I posted an answer, and then everyone else's answers disappeared and mine got a negative 1 downvote instantaneously? – Daryl Dec 23 '14 at 16:36
  • Not sure. I posted an answer for about 10 seconds but then I realized it used anonymous types and deleted it (since it doesn't apply to what you want). Your answer is good though - I'll upvote. – Jason Faulkner Dec 23 '14 at 16:38
  • @JasonFaulkner Ok, so you deleted yours. The other poster must have deleted theirs as well. It just was jarring to post an answer and then instantaneously lose both existing questions on the form. Thanks for your time! – Daryl Dec 23 '14 at 16:43
  • I believe that all attributes would be returned anyway, only to be discarded due to not being used, but that's just me not having a great opinion of the CRM LINQ provider. I'll have to do some benchmarking when I can to verify that gut feeling o'mine... – Alex Dec 23 '14 at 16:50

2 Answers2

5

I always do it like that:

var appt = (from a in context.AppointmentSet
            select new Appointment { 
                Attributes = 
                { 
                    { "modifiedon", a.ModifiedOn },
                    { "createdon", a.CreatedOn },
                    { "createdby", a.CreatedBy }
                }
            })
            .First();

It does not require any extra coding, I'm really surprised nobody posted that here yet.

Pawel Gradecki
  • 3,476
  • 6
  • 22
  • 37
  • it is ususally not considered as a good practice to preject on a mapped type. https://stackoverflow.com/questions/5325797/the-entity-cannot-be-constructed-in-a-linq-to-entities-query – tschmit007 Mar 06 '19 at 17:27
  • @tschmit007 I believe that that is an issue with the Entity Framework's Linq to Sql implementation. Does the fact that this is not using EF change your comment? I believe it does. – Daryl Mar 15 '19 at 23:16
0

This is the most efficient way I can think of:

Create a new constructor that accepts an AnonymousType (Object)

public Appointment(object anonymousType) : this()
{
    foreach (var p in anonymousType.GetType().GetProperties())
    {
        var value = p.GetValue(anonymousType);
        if (p.PropertyType == typeof(Guid))
        {
            // Type is Guid, must be Id
            base.Id = (Guid)value;
            Attributes["opportunityid"] = base.Id;
        }
        else if (p.Name == "FormattedValues")
        {
            // Add Support for FormattedValues
            FormattedValues.AddRange((FormattedValueCollection)value);
        }
        else
        {
            Attributes[p.Name.ToLower()] = value;
        }
    }
}

Then call it like so:

var appt = (from a in context.AppointmentSet
           select new Appointment(new { a.ModifiedOn })).First();

It has to reflect over all the properties of the AnoymousType, but it should work and has the added benefit of not having to rewrite the properties names each time.

Daryl
  • 18,592
  • 9
  • 78
  • 145