Flutter/Dart "Cannot modify an unmodifiable list" occurs when trying to sort a list that is the snapshot of a stream of lists - sorting

This is the code inside the builder method of the Streambuilder that causes the issue:
List<User> users = snapshot.data;
users.sort((user1, user2) => (user1.distanceInKm ?? 1000).compareTo(user2.distanceInKm ?? 1000));
If I use the following stream for the Streambuilder the above sorting works:
static Stream<List<User>> getUsersStreamWithDistance(
{#required User loggedInUser}) {
try {
var userSnapshots = _fireStore.collection('users').snapshots().map(
(snap) => snap.documents
.map((doc) => User.fromMap(map: doc.data))
.where((user) => user.email != loggedInUser.email)
.map((user) {
user.updateDistanceToOtherUser(otherUser: loggedInUser);
return user;
}).toList());
return userSnapshots;
} catch (e) {
print(e);
return null;
}
}
But not when I use the following stream, which is the one I need (ZipStream is from rxdart package):
static Stream<List<User>> getSpecifiedUsersStreamWithDistance(
{#required User loggedInUser, #required List<String> uids}) {
try {
List<Stream<User>> listOfStreams = [];
for (var uid in uids) {
Stream<User> streamToAdd = _fireStore
.collection('users')
.where('email', isEqualTo: uid)
.snapshots()
.map((snap) => snap.documents
.map((doc) => User.fromMap(map: doc.data))
.map((user) {
user.updateDistanceToOtherUser(otherUser: loggedInUser);
return user;
}).toList()[0]);
listOfStreams.add(streamToAdd);
}
Stream<List<User>> usersStream = ZipStream.list(listOfStreams);
return usersStream;
} catch (e) {
print(e);
return null;
}
}

Its because, ZipStream.list() creates a new Stream of List.unmodifiable() list.
List<User> users = List.from(snapshot.data); // to convert it editable list
users.sort((user1, user2) => (user1.distanceInKm ?? 1000).compareTo(user2.distanceInKm ?? 1000));

Related

Trying to create the database using ABP framework while DB Migration

I have inserted connectionstring in "[AbpTenantConnectionStrings]" table for same tenant id.
My objective is to create both the DB and migrage the DB
DB
while running below code only "Default Db is creating", can anyone let me know how to create and migrate other DB as well. what changes need to be done in the below code.
var tenants = await _tenantRepository.GetListAsync(includeDetails: true);
var migratedDatabaseSchemas = new HashSet<string>();
foreach (var tenant in tenants)
{
using (_currentTenant.Change(tenant.Id))
{
if (tenant.ConnectionStrings.Any())
{
var tenantConnectionStrings = tenant.ConnectionStrings
.Select(x => x.Value)
.ToList();
if (!migratedDatabaseSchemas.IsSupersetOf(tenantConnectionStrings))
{
await MigrateDatabaseSchemaAsync(tenant);
migratedDatabaseSchemas.AddIfNotContains(tenantConnectionStrings);
}
}
await SeedDataAsync(tenant);
}
After the below changes able to migrate or create the Db based on the connection-string inserted in "AbpTenantConnectionStrings":
public async Task MigrateAsync()
{
var initialMigrationAdded = AddInitialMigrationIfNotExist();
//var str = (await _connectionResolver.ResolveAsync("Default1"));
if (initialMigrationAdded)
{
return;
}
Logger.LogInformation("Started database migrations...");
await MigrateDatabaseSchemaAsync();
await SeedDataAsync();
Logger.LogInformation($"Successfully completed host database migrations.");
var tenants = await _tenantRepository.GetListAsync(includeDetails: true);
var migratedDatabaseSchemas = new HashSet<string>();
foreach (var tenant in tenants)
{
using (_currentTenant.Change(tenant.Id))
{
if (tenant.ConnectionStrings.Any())
{
var tenantConnectionStrings = tenant.ConnectionStrings
.Select(x => x.Value)
.ToList();
tenantConnectionStrings.ForEach(async s =>
{
await MigrateDatabaseSchemaAsync(s);
// migratedDatabaseSchemas.AddIfNotContains(s);
});
//if (!migratedDatabaseSchemas.IsSupersetOf(tenantConnectionStrings))
//{
// await MigrateDatabaseSchemaAsync(tenant);
// migratedDatabaseSchemas.AddIfNotContains(tenantConnectionStrings);
//}
}
await SeedDataAsync(tenant);
}
Logger.LogInformation($"Successfully completed {tenant.Name} tenant database migrations.");
}
Logger.LogInformation("Successfully completed all database migrations.");
Logger.LogInformation("You can safely end this process...");
}
private async Task MigrateDatabaseSchemaAsync(string str)
{
//Logger.LogInformation(
// $"Migrating schema for {(tenant == null ? "host" : tenant.Name + " tenant")} database...");
foreach (var migrator in _dbSchemaMigrators)
{
await migrator.MigrateAsync(str);
}
}
public interface IBookStoreDbSchemaMigrator
{
Task MigrateAsync();
Task MigrateAsync(string con);
}
public class EntityFrameworkCoreBookStoreDbSchemaMigrator
: IBookStoreDbSchemaMigrator, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public EntityFrameworkCoreBookStoreDbSchemaMigrator(
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task MigrateAsync()
{
/* We intentionally resolving the BookStoreMigrationsDbContext
* from IServiceProvider (instead of directly injecting it)
* to properly get the connection string of the current tenant in the
* current scope.
*/
await _serviceProvider.GetRequiredService<BookStoreMigrationsDbContext>()
.Database
.MigrateAsync();
}
public async Task MigrateAsync(string con)
{
var context = _serviceProvider
.GetRequiredService<BookStoreMigrationsDbContext>();
context.Database.SetConnectionString(con);
context.Database.Migrate();
}
}
Let me know if anyone has suggestions or a better approach.

how to implement Android In App BillingClient in Xamarin.Android Asynchronously

I am trying to implement below java code in c# referring to Android documentation
List<String> skuList = new ArrayList<> ();
skuList.add("premium_upgrade");
skuList.add("gas");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(BillingResult billingResult,
List<SkuDetails> skuDetailsList) {
// Process the result.
}
});
I have here 2 questions. I thought that i would run this code on a separate thread than UI thread like below to keep my ui responsive while network connection is done. is that the correct approach? QuerySkuDetailsAsync is called async but doesnt implement as async. how should this be working and how to handle in c# because it will fire and forget but Listener to handle the response.
public async Task<List<InAppBillingProduct>> GetProductsAsync(List<string> ProductIds)
{
var getSkuDetailsTask = Task.Factory.StartNew(() =>
{
var prms = SkuDetailsParams.NewBuilder();
var type = BillingClient.SkuType.Inapp;
prms.SetSkusList(ProductIds).SetType(type);
BillingClient.QuerySkuDetailsAsync(prms.Build(), new SkuDetailsResponseListener());
return InAppBillingProducts;
});
return await getSkuDetailsTask;
}
2nd question regarding how to handle with the listener as below. How do I return value from the listener. I need return list of InAppBillingProduct object.
public class SkuDetailsResponseListener : Java.Lang.Object, ISkuDetailsResponseListener
{
public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skus)
{
if (billingResult.ResponseCode == BillingResponseCode.Ok)
{
// get list of Products here and return
}
}
}
FYI. This is how I did it. This is not a complete code but this will give you and idea.
Listener - PCL
============
private async Task EventClicked()
{
var skuList = new List<string>();
skuList.Add("[nameofsubscriptionfoundinyourgoogleplay]");
if (await _billingClientLifecycle.Initialize(skuList, DisconnectedConnection))
{
var firstProduct = _billingClientLifecycle?.ProductsInStore?.FirstOrDefault();
if (firstProduct != null)
{
//purchase here
}
}
}
private void DisconnectedConnection()
{
//Todo.alfon. handle disconnection here...
}
Interface - PCL
===========
public interface IInAppBillingMigratedNew
{
List<InAppBillingPurchase> PurchasedProducts { get; set; }
List<InAppBillingProduct> ProductsInStore { get; set; }
Task<bool> Initialize(List<String> skuList, Action onDisconnected = null);
}
Dependency - Platform Droid
===============
[assembly: XF.Dependency(typeof(InAppBillingMigratedNew))]
public class InAppBillingMigratedNew : Java.Lang.Object, IBillingClientStateListener
, ISkuDetailsResponseListener, IInAppBillingMigratedNew
{
private Activity Context => CrossCurrentActivity.Current.Activity
?? throw new NullReferenceException("Current Context/Activity is null");
private BillingClient _billingClient;
private List<string> _skuList = new List<string>();
private TaskCompletionSource<bool> _tcsInitialized;
private Action _disconnectedAction;
private Dictionary<string, SkuDetails> _skusWithSkuDetails = new Dictionary<string, SkuDetails>();
public List<InAppBillingPurchase> PurchasedProducts { get; set; }
public List<InAppBillingProduct> ProductsInStore { get; set; }
public IntPtr Handle => throw new NotImplementedException();
public Task<bool> Initialize(List<string> skuList, Action disconnectedAction = null)
{
_disconnectedAction = disconnectedAction;
_tcsInitialized = new TaskCompletionSource<bool>();
var taskInit = _tcsInitialized.Task;
_skuList = skuList;
_billingClient = BillingClient.NewBuilder(Context)
.SetListener(this)
.EnablePendingPurchases()
.Build();
if (!_billingClient.IsReady)
{
_billingClient.StartConnection(this);
}
return taskInit;
}
#region IBillingClientStateListener
public void OnBillingServiceDisconnected()
{
Console.WriteLine($"Connection disconnected.");
_tcsInitialized?.TrySetResult(false);
_disconnectedAction?.Invoke();
}
public void OnBillingSetupFinished(BillingResult billingResult)
{
var responseCode = billingResult.ResponseCode;
var debugMessage = billingResult.DebugMessage;
if (responseCode == BillingResponseCode.Ok)
{
QuerySkuDetails();
QueryPurchases();
_tcsInitialized?.TrySetResult(true);
}
else
{
Console.WriteLine($"Failed connection {debugMessage}");
_tcsInitialized?.TrySetResult(false);
}
}
#endregion
#region ISkuDetailsResponseListener
public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skuDetailsList)
{
if (billingResult == null)
{
Console.WriteLine("onSkuDetailsResponse: null BillingResult");
return;
}
var responseCode = billingResult.ResponseCode;
var debugMessage = billingResult.DebugMessage;
switch (responseCode)
{
case BillingResponseCode.Ok:
if (skuDetailsList == null)
{
_skusWithSkuDetails.Clear();
}
else
{
if (skuDetailsList.Count > 0)
{
ProductsInStore = new List<InAppBillingProduct>();
}
foreach (var skuDetails in skuDetailsList)
{
_skusWithSkuDetails.Add(skuDetails.Sku, skuDetails);
//ToDo.alfon. make use mapper here
ProductsInStore.Add(new InAppBillingProduct
{
Name = skuDetails.Title,
Description = skuDetails.Description,
ProductId = skuDetails.Sku,
CurrencyCode = skuDetails.PriceCurrencyCode,
LocalizedIntroductoryPrice = skuDetails.IntroductoryPrice,
LocalizedPrice = skuDetails.Price,
MicrosIntroductoryPrice = skuDetails.IntroductoryPriceAmountMicros,
MicrosPrice = skuDetails.PriceAmountMicros
});
}
}
break;
case BillingResponseCode.ServiceDisconnected:
case BillingResponseCode.ServiceUnavailable:
case BillingResponseCode.BillingUnavailable:
case BillingResponseCode.ItemUnavailable:
case BillingResponseCode.DeveloperError:
case BillingResponseCode.Error:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
case BillingResponseCode.UserCancelled:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
// These response codes are not expected.
case BillingResponseCode.FeatureNotSupported:
case BillingResponseCode.ItemAlreadyOwned:
case BillingResponseCode.ItemNotOwned:
default:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
}
}
#endregion
#region Helper Methods Private
private void ProcessPurchases(List<Purchase> purchasesList)
{
if (purchasesList == null)
{
Console.WriteLine("No purchases done.");
return;
}
if (IsUnchangedPurchaseList(purchasesList))
{
Console.WriteLine("Purchases has not changed.");
return;
}
_purchases.AddRange(purchasesList);
PurchasedProducts = _purchases.Select(sku => new InAppBillingPurchase
{
PurchaseToken = sku.PurchaseToken
})?.ToList();
if (purchasesList != null)
{
LogAcknowledgementStatus(purchasesList);
}
}
private bool IsUnchangedPurchaseList(List<Purchase> purchasesList)
{
// TODO: Optimize to avoid updates with identical data.
return false;
}
private void LogAcknowledgementStatus(List<Purchase> purchasesList)
{
int ack_yes = 0;
int ack_no = 0;
foreach (var purchase in purchasesList)
{
if (purchase.IsAcknowledged)
{
ack_yes++;
}
else
{
ack_no++;
}
}
//Log.d(TAG, "logAcknowledgementStatus: acknowledged=" + ack_yes +
// " unacknowledged=" + ack_no);
}
private void QuerySkuDetails()
{
var parameters = SkuDetailsParams
.NewBuilder()
.SetType(BillingClient.SkuType.Subs)
.SetSkusList(_skuList)
.Build();
_billingClient.QuerySkuDetailsAsync(parameters, this);
}
private void QueryPurchases()
{
if (!_billingClient.IsReady)
{
Console.WriteLine("queryPurchases: BillingClient is not ready");
}
var result = _billingClient.QueryPurchases(BillingClient.SkuType.Subs);
ProcessPurchases(result?.PurchasesList?.ToList());
}
#endregion
}

Initializing[Unavailable#]-failed to lazily initialize a collection, no session or session was closed

I have one class called fixture which has two properties description and date. Now i dont want to operate directly on thi stwo propert but i wanted to operate on Some collection object "Allattribute" in this example which i m feeling while in get and set of each property.
retrival is working fine but while persisting i m getting this error
Initializing[Unavailable#]-failed to lazily initialize a collection, no session or session was closed.
Code for Model and Mapper is as below:
public class Fixture : EntityBase
{
public Fixture() {
//PogObject = new List<PogObject>();
//ObjectFixtureDate = new List<Objectfixturedate>();
}
public virtual long IDPOGObject { get; set; }
private ObservableCollection<DictionaryEntry> m_allattributes = new ObservableCollection<DictionaryEntry>();
private IList<FixtureDate> mFixtureDate = new List<FixtureDate>();
private IList<FixtureDesc> mFixtureDesc = new List<FixtureDesc>();
private IList<FixtureFlag> mFixtureFlag = new List<FixtureFlag>();
public virtual IList<FixtureDate> FixtureDate
{
get
{
mFixtureDate.Clear();
foreach (var item in m_allattributes)
{
if (item.Value is FixtureDate)
{
mFixtureDate.Add((FixtureDate)item.Value);
}
}
return mFixtureDate;
}
set
{
//try
//{
if (value.Count != 0)
{
foreach (var item in value)
{
DictionaryEntry dEntry = new DictionaryEntry(item.Dictionary.DictionaryName, item);
if (!m_allattributes.Contains(dEntry))
{
m_allattributes.Add(dEntry);
}
}
}
//}
//catch (Exception ex)
//{
//}
mFixtureDate = value;
}
}
public virtual IList<FixtureDesc> FixtureDescription
{
get
{
mFixtureDesc.Clear();
foreach (var item in m_allattributes)
{
if (item.Value is FixtureDesc)
{
if (!mFixtureDesc.Contains((FixtureDesc)item.Value))
{
mFixtureDesc.Add((FixtureDesc)item.Value);
}
}
}
return mFixtureDesc;
}
set
{
if (value.Count != 0)
{
foreach (var item in value)
{
DictionaryEntry dEntry = new DictionaryEntry(item.Dictionary.DictionaryName, item);
// DictionaryEntry dEntry = new DictionaryEntry(item.DescNum, item);
if (!m_allattributes.Contains(dEntry))
{
m_allattributes.Add(dEntry);
}
}
}
mFixtureDesc = value;
}
}
}
Mapper Class:
public class FixtureMap : ClassMap {
public FixtureMap() {
Table("SPOG_ObjectFixture");
Not.LazyLoad();
Id(x => x.IDPOGObject).GeneratedBy.Foreign("PogObject");
HasMany(x => x.FixtureDescription).Cascade.AllDeleteOrphan().Inverse().Not.LazyLoad().KeyColumn("IDPOGObject").Fetch.Join();
HasMany(x => x.FixtureDate).Cascade.AllDeleteOrphan().Inverse().Not.LazyLoad().KeyColumn("IDPOGObject");
}
}

Compare 2 lists using linq

I'm a linq noob.... can someone please some me how to achieve this using linq... I'm trying to compare 2 lists in both directions...
internal void UpdateUserTeams(int iUserID)
{
UserTeamCollection CurrentTeams = GetUserTeams(iUserID);
UserTeamCollection UpdatedTeams = this;
foreach (UserTeam ut in CurrentTeams)
{
if(!UpdatedTeams.ContainsTeam(ut.ID))
{
RemoveTeamFromDB();
}
}
foreach (UserTeam ut in UpdatedTeams)
{
if (!CurrentTeams.ContainsTeam(ut.ID))
{
AddTeamToDB();
}
}
}
public bool ContainsTeam(int iTeamID)
{
return this.Any(t => t.ID == iTeamID);
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Linqage
{
class Program
{
static void Main(string[] args)
{
UserTeamCollection currentTeams = new UserTeamCollection()
{
new UserTeam(1),
new UserTeam(2),
new UserTeam(3),
new UserTeam(4),
new UserTeam(5)
};
UserTeamCollection updatedTeams = new UserTeamCollection()
{
new UserTeam(2),
new UserTeam(4),
new UserTeam(6),
new UserTeam(8)
};
currentTeams.Except(updatedTeams).All(u =>
{
//Console.WriteLine("Item ID: {0}",u.ID);
//RemoveFromDB()
return true;
});
updatedTeams.Except(currentTeams).All(u =>
{
//Console.WriteLine("Item ID: {0}", u.ID);
//AddToDB()
return true;
});
}
}
public class UserTeamCollection
: List<UserTeam>
{
}
//Either overwrite the GetHashCode and Equals method OR create a IComparer
public class UserTeam
{
public int ID { get; set; }
public UserTeam(int id)
{
ID = id;
}
public override bool Equals(object obj)
{
UserTeam iOther = obj as UserTeam;
if (iOther != null)
{
return this.ID == iOther.ID;
}
return false;
}
public override int GetHashCode()
{
return ID.GetHashCode();
}
}
}
So converting your initial question to an english requirement:
foreach (UserTeam ut in CurrentTeams) // for each current team
{
if(!UpdatedTeams.ContainsTeam(ut.ID)) // that is not in the updated teams list
{
RemoveTeamFromDB(); // remove it from the database
}
}
foreach (UserTeam ut in UpdatedTeams) //for each of the teams in the updated teams list
{
if (!CurrentTeams.ContainsTeam(ut.ID)) //if the current team does not contain the updated team
{
AddTeamToDB(); //add the team to the database
}
}
Therefore, you want to do:
//select all current teams that are not in updated teams list
CurrentTeam.Except(UpdatedTeams).All(team => { RemoveTeamFromDB(team); return true; });
//select all updated teams that are not in the current teams list
UpdatedTeam.Except(CurrentTeams).All(team => { AddTeamToDB(team); return true; });
Make sure your UserTeam object has proper overrides for the Equals and GetHashCode methods, so that comparison between two UserTeams is accurate :)
You would normally use Enumerable.Except both ways to get the differences. Then add and remove as needed.
var addedTeams = UpdatedTeams.Except(CurrentTeams);
var removedTeams = CurrentTeams.Except(UpdatedTeams);
You're trying to get the outer parts from a full outer join. Here's a rough way to achieve that.
ILookup<int, UserTeam> currentLookup = CurrentTeams
.ToLookup(ut => ut.ID);
ILookup<int, UserTeam> updatedLookup = UpdatedTeams
.ToLookup(ut => ut.ID);
List<int> teamIds = CurrentTeams.Select(ut => ut.ID)
.Concat(UpdatedTeams.Select(ut => ut.ID))
.Distinct()
.ToList();
ILookup<string, UserTeam> results =
(
from id in teamIds
let inCurrent = currentLookup[id].Any()
let inUpdated = updatedLookup[id].Any()
let key = inCurrent && inUpdated ? "No Change" :
inCurrent ? "Remove" :
inUpdated ? "Add" :
"Inconceivable"
let teams = key == "Remove" ? currentLookup[id] :
updatedLookup[id]
from team in teams
select new {Key = key, Team = team)
).ToLookup(x => x.Key, x => x.Team);
foreach(UserTeam ut in results["Remove"])
{
RemoveTeamFromDB();
}
foreach(UserTeam ut in results["Add"])
{
AddTeamToDB();
}

Hierarchical structure iteration and LINQ

Assume that we have class
public class RMenuItem
{
public List<RMenuItem> ChildrenItems { get; }
public decimal OperationID { get; }
public string Name { get; }
}
as you can see - each menuitem could have children items - as usual in menu.
My task is to iterate through each items of this list and apply some action to it. Classical decision is to write recursive iteration. But I'm interesting if LINQ could make my task easier? For example, I suppose that we can write query that can get flat list of objects, which i can iterate simply with foreach. But my attempts in this way weren't successful yet.
So any help appreciated!
It's possible:
public void PrintAllNames(RMenuItem rootItem)
{
Action<RMenuItem> print = null;
print = m =>
{
Console.WriteLine(m.Name);
m.ChildrenItems.ForEach(print);
};
print(rootItem);
}
Notice how it's necessary to declare print so that print can use itself. This is directly comparable to a recursive method, which I'd rather use:
public void PrintAllNames(RMenuItem rootItem)
{
Console.WriteLine(rootItem.Name);
rootItem.ChildrenItems.ForEach(PrintAllNames);
}
(although for a more complex situation, maybe the functional solution would make the most sense)
I suggest 2 ways of achieving this. You can opt with an utility method to get all the items or you can implement the Visitor Pattern, though it implies changing the RMenuItem class.
Utility method:
static IEnumerable<RMenuItem> GetAllMenuItems(IList<RMenuItem> items)
{
if (items == null)
throw new ArgumentNullException("items");
Queue<RMenuItem> queue = new Queue<RMenuItem>(items);
while (queue.Count > 0)
{
var item = queue.Dequeue();
if (item.ChildrenItems != null)
{
foreach (var child in item.ChildrenItems)
{
queue.Enqueue(child);
}
}
yield return item;
}
}
I prefer an imperative way to a recursive because we can use iterator blocks.
Visitor Pattern:
public interface IRMenuItemVisitor
{
void Visit(RMenuItem item);
}
public class PrintRMenuItemVisitor : IRMenuItemVisitor
{
public void Visit(RMenuItem item)
{
Console.WriteLine(item);
}
}
public interface IRMenuItem
{
void Accept(IRMenuItemVisitor visitor);
}
public class RMenuItem : IRMenuItem
{
// ...
public void Accept(IRMenuItemVisitor visitor)
{
visitor.Visit(this);
if (ChildrenItems != null)
{
foreach (var item in ChildrenItems)
{
item.Accept(visitor);
}
}
}
}
Usage:
RMenuItem m1 = new RMenuItem
{
Name = "M1",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M11" },
new RMenuItem {
Name = "M12",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M121" },
new RMenuItem { Name = "M122" }
}
}
}
};
RMenuItem m2 = new RMenuItem
{
Name = "M2",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M21" },
new RMenuItem { Name = "M22" },
new RMenuItem { Name = "M23" }
}
};
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in GetAllMenuItems(menus))
{
Console.WriteLine(menu);
}
// or
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in menus)
{
menu.Accept(new PrintRMenuItemVisitor());
}
You could difine a Flatten method in your class (or as an extension if you prefer) like this
public IEnumerable<RMenuItem> Flatten()
{
foreach (var item in ChildrenItems)
{
yield return item;
}
return ChildrenItems.SelectMany(item => item.Flatten());
}
then doing somthing with each elements will be as simple as
RMenuItem rootItem ;
// do somthing with the root item
foreach (var item in rootItem.Flatten())
{
// do somthing
}
Indeed you can do that using LINQ, SelectMany flats out the list, just some example
menuItemsList.SelectMany(x => x.ChildrenItems).Where(c => c.someChildProperty);
Thanks
Edit:
In response to the comments, I was just giving an example of SelectMany previously. Thanks for pointing out.
menuItemsList.SelectMany(x => x.ChildrenItems.Select(p => p)).Where(c => c.someChildProperty);
OR something like this
menuItemsList.SelectMany(x => x.ChildrenItems).Select(p => p).Where(c => c.someChildProperty);
Edit2
Ahh .. now I understood what you want ..
We can just slightly modify my above query to do what you want
menuItemsList
.SelectMany(x => { //do something with x like printing it
x.ChildrenItems
})
.Select(p => { // do something with p like printing it
p
});
Basically you can do what you want the element inside the {}
Thanks

Resources