Dictionary Creation from a multi layer class - c#

I have the following model:
public class IP_BankInfo
{
public App.BankType BankType { get; set; }
public string FileExtension { get; set; }
public List<IP_BankRows> Rows { get; set; }
}
public class IP_BankRows
{
public int RowIndex { get; set; }
public List<IP_BankBindings> Bindings { get; set; }
}
public class IP_BankBindings
{
public int ColumnIndex { get; set; }
public string ExpectedHeader { get; set; }
public string TransactionPropertyName { get; set; }
}
Where for the post-processing, I am interested in IP_BankBindings - ColumnIndex and TransactionPropertyName. What I would like to do, is to create a new Dictionary<int,string> which will hold the above metioned.
I have tried to retrieve them via LINQ:
var items = info.Rows.Where(n => n.Bindings.Where(x => x.TransactionPropertyName.Length > 0));
However with no luck. Any suggestion would be welcome (maybe Dictionary is not the right type).
P.S. what I do afterwards is, that I have a CSV file which I read row by row, and these give me column index position and the target property I am interested in.

You can get the BankBindings using SelectMany()
var query = info.Rows
.SelectMany(r => r.Bindings)
.Where(x => x.TransactionPropertyName != null && x.TransactionPropertyName.Length > 0);
what I do afterwards is, that I have a CSV file which I read row by row, and these give me column index position and the target property I am interested in.
Do you mean TransactionPropertyName? To further get the ColumnIndex and TransactionPropertyName
var dataForCsv = query
.Select(b => new { b.ColumnIndex, b.TransactionPropertyName});
You can then loop through dataForCsv, writing the two properties to your CSV file.

Related

How to add items to existing list of objects?

I have three classes:
public class M2ArticleMain
{
public int Id { get; set; }
public List<M2ArticleAttributeWeb> Attribut_Web { get; set; }
}
public class M2ArticleAttributeWeb
{
public int Web_Id { get; set; }
public M2ArticleTmpMainSkus Variants { get; set; }
}
public class M2ArticleTmpMainSkus
{
public DateTime TimeAdded { get; set; }
public List<string> Skus { get; set; }
}
And I have two Lists in my code like this:
List<M2ArticleMain> data = new List<M2ArticleMain>();
List<M2ArticleAttributeWeb> attb = new List<M2ArticleAttributeWeb>();
In some part of my code firstly I (from foreach loop) add data to attb list where I add only only some data (because I don't have all data at this point), like this:
...
attb.Add(new M2ArticleAttributeWeb
{
Web_id = item.Id, //(item is from foreach loop)
Variants = null //this is **importat**, I left null for later to add it
});
Next, after I fill attb, I add all this to data list:
...
data.Add(new M2ArticleMain
{
Id = item.Id_Pk, //this is also from foreach loop,
Attribut_Web = attb //now in this part I have only data for Web_id and not Variants
}
Now my question is How to Add items later to data list to object Variants?
Something like this:
data.AddRange( "how to point to Variants" = some data);
The M2ArticleAttributeWeb type holding your Variants property is the member of a collection. That is, there are potentially many of them. You can reference an individual Variants property like this:
data[0].Attribut_Web[0].Variants
But you need to know which items you want to add map to which data and Attribut_Web indexes/objects in order to assign them properly. That probably means another loop, or even a nested loop. That is, you can see all of your Variants properties in a loop like this:
foreach(var main in data)
{
foreach(var attrw in main)
{
var v = attrw.Variants;
// do something with v
Console.WriteLine(v);
// **OR**
attrw.Variants = // assign some object
}
}
It's also much better practice to create your collection properties with the object, and then give them private set attributes:
public class M2ArticleMain
{
public int Id { get; set; }
public List<M2ArticleAttributeWeb> Attribut_Web { get; private set; } = new List<M2ArticleAttributeWeb>();
}
public class M2ArticleAttributeWeb
{
public int Web_Id { get; set; }
public M2ArticleTmpMainSkus Variants { get; set; }
}
public class M2ArticleTmpMainSkus
{
public DateTime TimeAdded { get; set; }
public List<string> Skus { get; private set; } = new List<string>();
}
Now instead of assigning Attribut_Web = attb, you would need to .Add() to the existing List.

New class selected by LINQ query, how to transfer to another model?

I'm really not understanding this as I've only dabbled in MVC and C#. I apologize if my terminology is wrong or confusing, I will do my best to answer questions. I have a couple models like so:
public class DataSharingModels
{
public string ReferenceID { get; set; }
public NBTC NBTCGroup { get; set; }
public Contractors ContractorsGroup { get; set; }
public Coordinators CoordinatorsGroup { get; set; }
public NGO NGOGroup { get; set; }
public Public PublicGroup { get; set; }
public SelectList FA_RA_List { get; set; }
}
public class NBTC
{
public String NBTC_FA_Centroid { get; set; }
public String NBTC_FA_Bound { get; set; }
public String NBTC_RA_Centroid { get; set; }
//more properties...
}
The DataSharingModels class contains the public NBTC NBTCGroup property. It is not public List<NBTC> NBTCGroup because there will only be one produced per instance of the controller being hit.
Now in my controller, I have a LINQ statement that selects a new NBTC class:
var nbtcVals = (from ds in db.SharingPermissions
where ds.FocalRefID.ToString() == ReferenceID
&& ds.ShareGroup == "NBTC"
select new NBTC
{
NBTC_FA_Centroid = ds.CIP_FA_Centroid,
NBTC_FA_Bound = ds.CIP_FA_Boundary,
NBTC_RA_Centroid = ds.CIP_RA_Centroid,
//more properties...
});
Where I'm going wrong is I would like to add that to my DataSharingModels model. I thought the nbtcVals type would be NBTC, but it's IQueryable<##.Models.NBTC>. I understand I could do this, but it seems redundant:
DataSharingModels dsm = new DataSharingModels();
if (nbtcVals.Any())
{
foreach (var i in nbtcVals)
{
dsm.NBTCGroup.NBTC_FA_Centroid = i.NBTC_FA_Centroid;
dsm.NBTCGroup.NBTC_FA_Boundary = i.NBTC_FA_Bound;
dsm.NBTCGroup.NBTC_RA_Centroid = i.NBTC_RA_Centroid;
//more properties...
}
}
What is a more direct way to do this? There must be one. I supposed I could also return an anonymous type in the LINQ query and then assign each property in the foreach like dsm.NBTCGroup.NBTC_RA_Centroid = i.NBTC_RA_Centroid but that seems the same as the other way.
var nbtcgroup = (from ds in db.SharingPermissions
where ds.FocalRefID.ToString() == ReferenceID
&& ds.ShareGroup == "NBTC"
select new NBTC
{
NBTC_FA_Centroid = ds.CIP_FA_Centroid,
NBTC_FA_Bound = ds.CIP_FA_Boundary,
NBTC_RA_Centroid = ds.CIP_RA_Centroid,
//more properties...
})
.OrderByDescending(n => n.Id) // or some other property that could identify sorting
.FirstOrDefault();
This one has a translation to SQL (LIMIT or TOP depending on backend).

ravendb index across multiple nested properties

I am asking how to create an index based upon two different nested properties on an document. I am executing these queries through C#.
public class LocationCode
{
public string Code {get;set;}
public string SeqId {get;set;}
}
public class ColorCode
{
public string Code {get;set;}
public string SeqId {get;set;}
}
public class TestDocument
{
public int Id {get;set;}
public List<LocationCode> Locations { get; set; }
public List<ColorCode> Colors { get; set; }
}
I have experimented with various AbstractIndexCreationTask, Map, and Map+Reduce, but to no avail.
I would like to be able to do a query such as:
Get all documents where any Locations.Code property is "USA", AND/OR Colors.Code="RED", or on the SeqId property. I dont know whether this would mean I need multiple indexes. Normally I would either be comparing the Code property on both nested classes, or the Seq, but never mixed.
Please could someone point me in the right direction.
Many thanks
Phil
Create your index like this:
public class TestIndex : AbstractIndexCreationTask<TestDocument, TestIndex.IndexEntry>
{
public class IndexEntry
{
public IList<string> LocationCodes { get; set; }
public IList<string> ColorCodes { get; set; }
}
public TestIndex()
{
Map = testDocs =>
from testDoc in testDocs
select new
{
LocationCodes = testDoc.Locations.Select(x=> x.Code),
ColorCodes = testDoc.Colors.Select(x=> x.Code)
};
}
}
Then query it like this:
var q = session.Query<TestIndex.IndexEntry, TestIndex>()
.Where(x => x.LocationCodes.Any(y => y == "USA") &&
x.ColorCodes.Any(y => y == "RED"))
.OfType<TestDocument>();
Full unit test here.

How to move string into ViewModel

I am creating a new ViewModel that tally's up the results of a survey, performers some calculations on that data, and then returns the new calculation to a view. I cannot figure out how to include regular "string" data in the collection.
var data = from SurveyResponseModel in db.SurveyResponseModels
group SurveyResponseModel by SurveyResponseModel.MemberId into resultCount
select new ResultsViewModel()
{
MemberId = resultCount.Key,
UseNewTreatmentResult = db.SurveyResponseModels.Count(r => r.UseNewTreatment),
UseBetterTechniqueResult = db.SurveyResponseModels.Count(r => r.UseBetterTechnique),
ChangesOthersResult = db.SurveyResponseModels.First(r => r.ChangesOthers),
};
return View(data);
The first part is counting boolean responses and passing them as an integer back to the ViewModel. The section that includes ChangesOthersResult = db.SurveyResponseModels.First(r => r.ChangesOthers), Should just select the strings from the Model and pass to the ViewModel. I am currently getting a syntax error about changing from type string to bool. I'm not sure what the syntax for this is.
public class SurveyResponseModel
{
[Key]
public int ResponseId { get; set; }
public int MemberId { get; set; }
public int ProgramId { get; set; }
[DisplayName("Use a new treatment")]
public bool UseNewTreatment { get; set; }
[DisplayName("Use better/more updated technique")]
public bool UseBetterTechnique { get; set; }
[DisplayName("Other (please specify):")]
public string ChangesOthers { get; set; }
}
public class ResultsViewModel
{
public int MemberId { get; set; }
public int ProgramId { get; set; }
[DisplayName("Use a new treatment")]
public int UseNewTreatmentResult { get; set; }
[DisplayName("Use better/more updated technique")]
public int UseBetterTechniqueResult { get; set; }
[DisplayName("Other (please specify):")]
public string ChangesOthersResult { get; set; }
}
You need:
ChangesOthersResult = db.SurveyResponseModels.Select(r => r.ChangesOthers)
Or SelectMany. Eventually add FirstOrDefault() at the end depending on what type ChangesOthersResult is and what you actually want to select.
Select gives you a "collection of collections" (I am assuming that ChangesOthers is a collection type). SelectMany a "flattened collection" of the generic type of your ChangesOthers collection. Adding FirstOrDefault() after Select gives you the single collection of the first SurveyResponseModels entity - or null.
Edit
After you supplied the classes I see that ChangesOthers and ChangesOthersResult aren't collections but just of type string. So the line should be:
ChangesOthersResult = db.SurveyResponseModels.Select(r => r.ChangesOthers)
.FirstOrDefault()

Looping through (IEnumerable) results and updating, but nothing updated?

I am looping thru parent records ("europe") and updating its <Ienumerable> field called "childPublication" with its child records. But the childPublication is null after the loop and assignment?
Here's my code:
foreach (var e in europe)
{
string child = e.HasChild ?? "";
if (child.Contains("True"))
{
IEnumerable<Publication> eChildrens = children.OfType<Publication>()
.Where(ep => ep.ParentID.Equals(e.PublicationId));
if (eChildrens.Count() > 0)
{
e.ChildPublication = eChildrens;
}
}
}
member.EuropeMiddleEastAfricaPublication = europe;
public class Publication
{
public int PublicationId { get; set; }
public int ContentTypeId { get; set; }
public string PublicationName { get; set; }
public string PublicationFullName { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
public string URL { get; set; }
public string CountryId { get; set; }
public string LanguageId { get; set; }
public string Active { get; set; }
public string Subscription { get; set; }
public string ClientOnly { get; set; }
public string PrintVersion { get; set; }
public string EmailVersion { get; set; }
public string RegisteredforPrint { get; set; }
public string RegisteredforEmail { get; set; }
public int ParentID { get; set; }
public string HasChild { get; set; }
public IEnumerable<Publication> ChildPublication { get; set; }
}
First, you have eChildren = children, so I'm assuming children is passed in somewhere?
I would probably code it something like:
foreach (var e in europe)
{
// .Net 4.0 use: string.IsNullOrWhiteSpace()
if (!string.IsNullOrEmpty(e.HadChild)
// I prefer IndexOf which allows Culture and IgnoreCase
&& e.HasChild.IndexOf("True", StringComparison.CurrentCultureIgnoreCase))
{
IEnumerable<Publication> eChildrens =
children.OfType<Publication>()
.Where(ep => ep.ParentID.Equals(e.PublicationId))
.ToList(); //Force the IEnumeration to Enumerate.
if (eChildrens.Count() > 0)
{
e.ChildPublication = eChildrens;
}
}
}
You should debug your program and verify that you are actually getting inside your if and setting the property. If you aren't, then it will absolutely be null. But note that you are doing something dangerous anyway by closing over the loop variable.
IEnumerable<Publication> eChildrens =
children.OfType<Publication>().Where(ep =>
ep.ParentID.Equals(e.PublicationId));
if (eChildrens.Count() > 0)
{
e.ChildPublication = eChildrens;
}
eChildrens is a lazily evaluated query and it is capturing the loop variable e. When you get outside the query and try to use the results, unless you have weird expectations, you code is not going to do what you want. In a closure, it is the variable that is captured, so your query will always be looking at the same var e when you get outside of the loop. You're going to have a lot of objects looking at the wrong ChildPublication sequences.
To avoid this issue, either create a local temporary variable inside the loop and close over that
var temp = e; // local temporary variable, used below
IEnumerable<Publication> eChildrens =
children.OfType<Publication>().Where(ep =>
ep.ParentID.Equals(temp.PublicationId));
if (eChildrens.Count() > 0)
{
e.ChildPublication = eChildrens;
}
Or alternately force evaluation of the query by invoking a method such as ToList();
IEnumerable<Publication> eChildrens =
children.OfType<Publication>().Where(ep =>
ep.ParentID.Equals(e.PublicationId)).ToList();
if (eChildrens.Count() > 0)
{
e.ChildPublication = eChildrens;
}
For more on this topic, please read Eric Lippert's blog entry.
Here is the problem. IEnumerable users an iterator run against your data source. Iterators return a new object, not a reference to the original object.
So, you are making changes on a copy of your original data, not your actual data. If you want to modify your original data, you need to pass a reference to it and work with that reference.
Have you tried using a List<Publication> instead or IEnumerable<Publication>. I always have much more sucess with it for simple collection handling. IEnumerable often times wants an Enumerator defined which is a little more overhead.

Categories