-2

I am trying to convert my class to a IEnumerable object.

My class is:

public class StageTwo : IEnumerable
{
    public string Party { get; set; }
    public string Currency { get; set; }
    public string DrCr { get; set; }
    public string Account { get; set; }
    public double? Amount { get; set; }

    public IEnumerator GetEnumerator()
    {
        return GetEnumerator();
    }
}

Below I have a method where I query some data and place it successfully into my StageTwo object. Then I want to duplicate most of the data and changes -item.Amount.

 public IEnumerable<StageTwo> Stage2Entries()
        {
            var queryJoin = (from inn in db.Input.Take(1)
                             join y in db.InputY on inn.YAction equals y.YAction
                             orderby inn.Id descending
                             select new StageTwo
                             {
                                 Id = inn.Id,
                                 Party = inn.XParty,
                                 Currency = inn.Curr,
                                 DrCr = y.DrAccount1,
                                 Account = y.YAction,
                                 Amount = inn.Amount
                             });
/*WORKS FINE ABOVE*/

            StageTwo stageTwoFinal = new StageTwo();
            foreach (var item in queryJoin)
            {
                stageTwoFinal.Id = item.Id;
                stageTwoFinal.Party = item.Party;
                stageTwoFinal.Currency = item.Currency;
                stageTwoFinal.DrCr = item.DrCr;
                stageTwoFinal.Account = item.Account;
                stageTwoFinal.Amount = item.Amount;

                stageTwoFinal.Id = item.Id;
                stageTwoFinal.Party = item.Party;
                stageTwoFinal.Currency = item.Currency;
                stageTwoFinal.DrCr = item.DrCr;
                stageTwoFinal.Account = item.Account;
                stageTwoFinal.Amount = -item.Amount;
            }
/*Unable to convert here*/
            var collection = (IEnumerable<StageTwo>)stageTwoFinal;
            return stageTwoFinal;
        }

I am getting a System.InvalidCastException

How can I convert this class to an IEnumerable?

Viggo Lundén
  • 696
  • 1
  • 6
  • 30
NoobCoder
  • 117
  • 1
  • 11
  • Why convert it to an IEnumerable? It's a single object, not a list of objects. That `public IEnumerator GetEnumerator()` calls itself, resulting in infinite recursion – Panagiotis Kanavos Nov 28 '19 at 12:13
  • Your `StageTwo`-class already **is** enumerable. So an `IEnumerable` effectivly is a list of lists. Anyway usually don´t need to implement your own collections, the existsing ones are pretty fine. Omit the `: IEnumerable` from your `StageTwo`-class and you´re fine. – HimBromBeere Nov 28 '19 at 12:14
  • Maybe you can explain what you're trying to do. You can add your StageTwo to a list, then the list with the item is an IEnumerable. Atm your object is an IEnumerable not generic. – Charles Nov 28 '19 at 12:14
  • 4
    What are you trying to do? This code is full of bugs, overwrites the *same* object in a loop. If you wanted to return the results just write `return queryJoin.ToList()` – Panagiotis Kanavos Nov 28 '19 at 12:15
  • Your `StageTwo` class only implements `IEnumerable` not the `IEnumerable` interface you're trying to cast to. Implement the generic version if this is what you want, although it looks like you really want to use `queryJoin.Select(item => new StageTwoFinal { ... })` instead. – Lee Nov 28 '19 at 12:16
  • You can make a `new [] { stageTwoFinal }.AsEnumerable()`, but I guess you do not want an `IEnumerable` as it makes no sense in this context. Also, why did you set the Id one time, then just after that you've overriden that with the same value? I do not think that duplicate code is by accident. `stageTwoFinal.Id = item.Id` – Bruno Miquelin Nov 28 '19 at 12:16
  • 1
    Why do you assign the same properties twice within your loop? Why do you even **have** that loop? You can simply use `return queryJoin`. – HimBromBeere Nov 28 '19 at 12:17
  • What Panagiotis Kanavos said + no need to use `StageTwo : IEnumerable`, just drop the `: IEnumerable` part + drop the `public IEnumerator GetEnumerator() {...}` which is also not needed. Your class is the *item in the collection*, but by this you are attempting to make each *item behave like a collection*, which makes no sense in this case. – Peter B Nov 28 '19 at 12:20

2 Answers2

2

If you want to return the query results just write :

return queryJoin.ToList()

That executes the query and returns the results as a list. You need nothing more than :

public IEnumerable<StageTwo> Stage2Entries()
{
    var queryJoin = (from inn in db.Input.Take(1)
                         join y in db.InputY on inn.YAction equals y.YAction
                         orderby inn.Id descending
                         select new StageTwo
                         {
                             Id = inn.Id,
                             Party = inn.XParty,
                             Currency = inn.Curr,
                             DrCr = y.DrAccount1,
                             Account = y.YAction,
                             Amount = inn.Amount
                         });
    return queryJoin.ToList();
}

IEnumerable<T> is the interface implemented by all collections. StageTwo represents a a single object though so it doesn't make any sense for it to implement IEnumerable<T>

If you use EF/EF Core, you can execute the query asynchronously (ie without blocking) with ToListAsync() :

public async Task<IEnumerable<StageTwo>> Stage2Entries()
{
    ...
    var list=await queryJoin.ToListAsync();
    return list;
}
Panagiotis Kanavos
  • 90,087
  • 9
  • 138
  • 171
0

You don´t need to implement IEnumerable or its generic version IEnumerable<T> in order to put elements of your class into a collection. Having said this you probably don´t want to implement the interface:

public class StageTwo
{
    public string Party { get; set; }
    public string Currency { get; set; }
    public string DrCr { get; set; }
    public string Account { get; set; }
    public double? Amount { get; set; }
}

In other words your StageTwo-instances aren´t collections themeselves. Instead they are just elements within a collection.

Now you can create instances of that class within your query and return that query:

var queryJoin = (from inn in db.Input.Take(1)
                         join y in db.InputY on inn.YAction equals y.YAction
                         orderby inn.Id descending
                         select new StageTwo
                         {
                             Id = inn.Id,
                             Party = inn.XParty,
                             Currency = inn.Curr,
                             DrCr = y.DrAccount1,
                             Account = y.YAction,
                             Amount = inn.Amount
                         });
return queryJoin;

You see you don´t need all that looping and casting at all, because queryJoin already is an IEnumerable<StageTwo>.

Be aware that the query is executed deferredly, which means it runs when you iterate the elements (e.g. by using a foreach or ToList). So you don´t see the results of that query imediately, you have to explicitely execute that query. See Deferred execution in C# for an explanation on this.

HimBromBeere
  • 32,045
  • 4
  • 46
  • 84