-1

I have a Queryable<Foo> and a List<Bar>. I'm already able to map which books are related to which Authors using LINQ.

I would like to use values of the Bar and dynamically add properties to the Foo object as shown below, obviously changing the type to object or dynamic.

Is this possible with LINQ, and if so, how can I do it?

Sample Desired Result (eventually parsed to JSON)

{
    "Id" : 1,
    "Name" : "John Doe",
    "Garden" : 7,
    "Pilot" : 4
}

Sample Classes

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Bar
{
    public string Title { get; set; }
    public int Count { get; set; }
}
jacobvoller.com
  • 476
  • 7
  • 28
  • 7
    Where is relation between author and book? Also structure of your json result is very strange and not very comfortable to work with. I suggest to use array of books. – Sergey Berezovskiy Jan 11 '17 at 16:43
  • I already have the two mapped correctly so I didn't include that in the same classes. – jacobvoller.com Jan 11 '17 at 16:46
  • How? At the very least your book should have an author. Otherwise how do you associate an author with a book – Ian Murray Jan 11 '17 at 16:49
  • 1
    Why not an array of books instead of properties for each book? – Luis Lavieri Jan 11 '17 at 16:49
  • @SergeyBerezovskiy These are not the actual object names and fields I am using, I simplified it for the example. Would it be more comfortable if edited the OP and made the classes Foo and Bar? – jacobvoller.com Jan 11 '17 at 16:50
  • 2
    I agree with Sergey, however I'm also confused what you mean about "dynamically add properties to the Author object". Do you mean extend the C# `Author` class, or add properties to the JSON object? – krillgar Jan 11 '17 at 16:50
  • Agree with @LuisLavieri - what happens if an author has 27 books? 100? A collection of books instead of a dynamic number of properties would be cleaner and easier. – D Stanley Jan 11 '17 at 16:51
  • After your edit I think your best bet is to create a dictionary using a loop (not Linq) and render JSON from that. – D Stanley Jan 11 '17 at 16:56
  • @JacobVoller and here go two books - one named "Name", and another named "Id". Woo-hoo – Sergey Berezovskiy Jan 11 '17 at 16:58
  • @SergeyBerezovskiy I understand what you're saying, but I'm asking a question about how to accomplish an end result with LINQ, the application of the answer is drastically different then books and authors. – jacobvoller.com Jan 11 '17 at 17:01
  • @DStanley I need to use LINQ because the list has not been materialized yet and I need to return an IQueryable object so that further processing may be done – jacobvoller.com Jan 11 '17 at 17:02
  • @JacobVoller then it still not clear how authors and books related. You have Queryable of authors. collection. not one. and list of books. Which of those books related to second author? – Sergey Berezovskiy Jan 11 '17 at 17:03
  • 1
    seems like you can search for something like "ExpandoObject to JSON" http://www.patridgedev.com/2011/08/24/getting-dynamic-expandoobject-to-serialize-to-json-as-expected/ – Slai Jan 11 '17 at 17:04
  • @SergeyBerezovskiy The relation isn't important to the question. The question is how to expand upon an object using LINQ – jacobvoller.com Jan 11 '17 at 17:05
  • @JacobVoller You can't add properties to existing classes. If you want to generate the JSON you describe you'll have to use an anonymous type or some other structure. What type of "further processing" are you talking about? – D Stanley Jan 11 '17 at 17:15
  • @DStanley Yes I know that I cannot add properties to existing classes. The OP says that converting to dynamic, object, or ExpandoObject is okay and expected. – jacobvoller.com Jan 11 '17 at 17:16
  • @JacobVoller Then you won't be able to do "additional processing". That's the part that's confusing to me. Once you "convert" it - it's no longer a `Foo`. Also you can't "convert" to `object` or `dynamic` - those just change how the _compiler_ treats the object. Underneath it's still a `Foo`. – D Stanley Jan 11 '17 at 17:17
  • @DStanley If I can convert Foo to ExpandoObject, why would I not be able to add new properties? – jacobvoller.com Jan 11 '17 at 17:22
  • @JacobVoller You can, but then you can't do "additional processing" as if it were a `Foo`, because it's not. – D Stanley Jan 11 '17 at 17:22
  • @DStanley that's okay, can you post an example? – jacobvoller.com Jan 11 '17 at 17:25
  • 1
    I mention how to add properties in my answer here: http://stackoverflow.com/questions/15819720/dynamically-add-c-sharp-properties-at-runtime/15819760#15819760 which you could of course make use of during whatever linq transform you're performing. – Clint Jan 11 '17 at 17:33
  • After all these comments the question itself is still utterly unclear. Foo, Bar, Book, Author, Garden, Pilot... It's a pile of unrelated entities and properties. – Gert Arnold Dec 18 '20 at 08:34

3 Answers3

2

You can not dynamically add properties to classes. I can see a couple of way you could possibly get the desired result.

Simplest would be to amend you classes as

public class Author
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Book> AuthorBooks { get; set; }
}

public class Book
{
    public string Title { get; set; }
    public string Isbn { get; set; }
}

However this would give you a slightly different structure on your JSON

The Other way would be to use an ExpandoObject, providing you are using .net.4.0 or above. ExpandoObject allows you to add properties as it is based off the Dynamic class.

You can get more information here https://blogs.msdn.microsoft.com/csharpfaq/2009/09/30/dynamic-in-c-4-0-introducing-the-expandoobject/

Hope this Helps

Edit

You could try something like this

FooList.ForEach((x) =>
            {
                //Define a new expando
                dynamic NewClass = new ExpandoObject();
                NewClass.Id = x.Id;
                NewClass.Name = x.Name;
                //Get the relating Bar Record
                BarList.Where(b=> b.FooId == x.Id).ToList().ForEach((b) =>
                {
                    NewClass[b.Title] = b.Count;
                });
                //This bit Depends on how you want the Json
                using (TextWriter writer = System.IO.File.CreateText("YourFilepat/Here"))
                {
                    var serializer = new JsonSerializer();
                    serializer.Serialize(writer, NewClass);
                }

            });

The JsonSerializer is from Newtonsoft.Json

DotNetGeek
  • 21
  • 3
  • The ExpandObject looks like it could work. Do you have any examples of how this might be done? -- Disregarding the List, and sticking strictly to the original question – jacobvoller.com Jan 11 '17 at 17:04
1

Have you tried using anonymous types?

var foos = new List<Foo>{
    new Foo{Id = 1, Name = "Grapes of Wrath"},
    new Foo{Id = 2, Name = "Shantaram"}
};

var anonObject = from f in foos
                    select new
                    {
                        Id = f.Id,
                        Name = f.Name,
                        Garden = 6
                    };
var serialized = JsonConvert.SerializeObject(anonObject);
return serialized; //[{"Id":1,"Name":"Grapes of Wrath","Garden":6},
    //{"Id":2,"Name":"Shantaram","Garden":6}]

Mind you that anonymous types cannot cross the function boundary, so you'd have to create your dynamic properties and serialize in the same function.

Alternately, you can just create the new type like this:

public class FooBar
{
    public Foo Foo { get; set; }
    public List<Bar> Bars { get; set; }
}

And this guy can move everywhere, has all the properties you need, won't need to be changed every time you change Foo or Bar, and will cleanly serialize.

Guillaume CR
  • 3,006
  • 1
  • 19
  • 31
  • I like this and think it could be on the right track, but 'Foo' has a lot of properties that will change over time. I don't want to have to add every new field in the select every time. Additionally, serializing in the same method is not an option – jacobvoller.com Jan 11 '17 at 19:15
  • @JacobVoller If the types need to exist in different parts of the app, maybe you need to bite the bullet and create the classes. If this "dynamic" class needs the same properties as Foo or Bar, then just inherit from them, no? – Guillaume CR Jan 11 '17 at 19:25
  • The types dont need to exist in different parts of the app, the propertys/Fields just need to be on an object- which can be dynamic – jacobvoller.com Jan 11 '17 at 19:26
  • @JacobVoller In that case you may want to consider returning the anonymous type as a System.Object. It has the properties, and they can be discovered, which is what JsonConvert does. Strong typing is usually preferred for maintenance reasons, but dynamic types are not any stronger than System.Object in that regards. – Guillaume CR Jan 11 '17 at 19:32
  • @JacobVoller I edited my answer because it sounds like you are trying really hard to avoid create the new type, but I share the opinion of other commenters here that the new class is the cleanest solution. – Guillaume CR Jan 11 '17 at 20:04
  • Thank you for your opinions, unfortunately a new class is not an option as this is for industry level software that already exists. – jacobvoller.com Jan 11 '17 at 21:26
0

If you are looking to dynamically add a property to an object this could be a solution.

This is what has worked for me, I also had a concern and it was what happened with those domain objects that had many properties, the maintainability for any changes in the object was absurd, I managed to build an implementation with LINQ - ExpandObject - Reflection, which helped to keep my object dynamic and only add the additional properties that my view logic required.

var expandedModel = FooList.Select(x =>
                    {
                        dynamic expandObject = new ExpandoObject();
                        expandObject.NewProperty= $"PropertyValue";
                        foreach (var property in x.GetType().GetProperties())
                        {
                            ((IDictionary<string, object>)expandObject).Add(property.Name, property.GetValue(x));
                        }
                        return expandObject;
                    }).ToList();