How to use a dictionary property in C# using LINQ - c#

While I userstand how get; & set; with simple types such as strings now can more properties like Dictionary be get or set or can they?
I have a small dos programe trying to do this. snippet below.
# User.cs
namespace LDIFMod
{
public class User
{
public string UserHash { get; set; }
public string UserID { get; set; }
public Dictionary<string, string> UserDict { get; set; } <- how to do this???
}
}
and in my Program.cs
var query = from line in File.ReadAllLines(args[0])
let UserRecord = line.Split(',')
select new User()
{
UserHash = UserRecord[2].Trim() +UserRecord[3].Trim(),
UserID = UserRecord[4].Trim(),
UserDict.???? // userDict should contain key = UserRecord[5] & value = UserRecord[9]
}

You will need to first initialize the dictionary in the constructor of the user class. Use the private set to prevent people from re-initializing the dictionary.
# User.cs
namespace LDIFMod
{
public class User
{
User()
{
UserDict = new Dictionary<string, string>()
}
public string UserHash { get; set; }
public string UserID { get; set; }
public Dictionary<string, string> UserDict { get; private set; }
}
}
Your calling code becomes
var query = from line in File.ReadAllLines(args[0])
let UserRecord = line.Split(',')
select new User()
{
UserHash = UserRecord[2].Trim() +UserRecord[3].Trim(),
UserID = UserRecord[4].Trim(),
UserDict.Add(UserRecord[5],UserRecord[9]);
}
This returns one dictionary per query row. if you want all of the rows to share a dictionary you will need to make it static or not store it inside User. If you do this be aware that linq is delayed execution so the dictionary will not be fully populated until after you fully enumerate the query.
I thought I would give a example of how to do it with all of them in a single dictionary.
# User.cs
namespace LDIFMod
{
public class User
{
public string UserHash { get; set; }
public string UserID { get; set; }
public readonly string[] SourceData {get; private set;}
}
}
and here is the query
var query = from line in File.ReadAllLines(args[0])
let UserRecord = line.Split(',')
select new User()
{
UserHash = UserRecord[2].Trim() + UserRecord[3].Trim(),
UserID = UserRecord[4].Trim(),
SourceData = UserRecord;
}
var UserLookp = query.ToDictionary((user) => user.SourceData[5], (user) => user.SourceData[9]);
This is purely from memory without a ide to check for bugs so there could be some errors.

You can use the collection initializer to create the dictionary:
{
UserHash = UserRecord[2].Trim() +UserRecord[3].Trim(),
UserID = UserRecord[4].Trim(),
UserDict = new Dictionary<string, string> { { UserRecord[5], UserRecord[9] } }
}
Also, see here: http://msdn.microsoft.com/en-us/library/bb531208.aspx

Related

How to combine two flat lists into one nested object

I have three lists which contains three same properties in each collection. I want to combine a result into one collection. Ex classes structure is as below
public class Order
{
public int ProductId { get; set; }
public int CustomerId { get; set; }
public int OrderId { get; set; }
// Few other Properties of OrderDetail
}
public class PaymentDetail
{
public int ProductId { get; set; }
public int CustomerId { get; set; }
public int OrderId { get; set; }
// Few other Properties form PaymentDetail
}
public class CouponUsageDetail
{
public int ProductId { get; set; }
public int CustomerId { get; set; }
public int OrderId { get; set; }
// Few other Properties form CouponUsageDetail
}
This type of output is coming from one API service where each class is in form of list object (JSON format) and we need to perform some operations on this. All three properties (ProductId,CustomerId, OrderId) contains same values in each collection which means all three properties are repeated in each collection. We need to perform some look ups on these collections. So in normal way what we can do with it is as - start a foreach from Order list and filter all three matching properties of PaymentDetail and CouponUsageDetail. But it would be costlier in term of performance when the data size is increased. So thought of making nesting structure upfront and avoid lookups. If we make the output nested as below this will help to avoid lookups on PaymentDetail and CouponUsageDetail.
Ex - we are receiving JOSN in below format
{"Orders":[{"ProductId":301,"CustomerId":101,"OrderId":201},{"ProductId":701,"CustomerId":501,"OrderId":601}],"PaymentDetails":[{"ProductId":301,"CustomerId":101,"OrderId":201},{"ProductId":701,"CustomerId":501,"OrderId":601}],"CouponUsageDetails":[{"ProductId":301,"CustomerId":101,"OrderId":201},{"ProductId":701,"CustomerId":501,"OrderId":601}]}
and with this output we want to form object as
public class OrderDetails
{
public int ProductId { get; set; }
public int CustomerId { get; set; }
public int OrderId { get; set; }
// Few other Properties of OrderDetail
List<PaymentDetail> PaymentDetail { get; set; }
List<CouponUsageDetail> CouponUsageDetail { get; set; }
}
Can you guide, what would be the best optimum usage of linq where we can combine all these three matching properties and make it just one nested structure better?
Thank You!
Note: I know this structure needs to be normalized but please ignore the normalization rule here as this is not in our control.
What are you describing sounds like two standard multi-key LINQ group joins. They are quite efficient (LINQ to Objects implementation uses prepared fast hash based lookups), so no further optimizations are needed:
var orderDetails = (
from o in data.Orders
join p in data.PaymentDetails
on new { o.ProductId, o.CustomerId, o.OrderId }
equals new { p.ProductId, p.CustomerId, p.OrderId }
into orderPaymentDetails
join c in data.CouponUsageDetails
on new { o.ProductId, o.CustomerId, o.OrderId }
equals new { c.ProductId, c.CustomerId, c.OrderId }
into orderCouponUsageDetails
select new OrderDetails
{
ProductId = o.ProductId,
CustomerId = o.CustomerId,
OrderId = o.OrderId,
// Few other Properties of OrderDetail
PaymentDetail = orderPaymentDetails.ToList(),
CouponUsageDetail = orderCouponUsageDetails.ToList(),
})
.ToList();
There seems to be a number of questions here combined, I'll try to work through them:
Data Models and Deserialization
With respect to generating a single structure from your API response, I would recommend using the Newtonsoft.Json libraries, available on NuGet Json.NET. They will allow you to deserialize the response from your API, into a single object, which given the sample you provided, should contain a collection of each of your models, Order, PaymentDetail, CouponUsageDetail:
public class APIResponceContainer
{
[JsonProperty("Orders")]
public List<Order> Orders { get; set; }
[JsonProperty("PaymentDetails")]
public List<PaymentDetail> PaymentDetails { get; set; }
[JsonProperty("CouponUsageDetails")]
public List<CouponUsageDetail> CouponUsageDetails { get; set; }
public APIResponceContainer()
{
Orders = new List<Order>();
PaymentDetails = new List<PaymentDetail>();
CouponUsageDetails = new List<CouponUsageDetail>();
}
}
Be aware to add the required attributes to each of your models as so:
public class Order
{
[JsonProperty("ProductId")]
public int ProductId { get; set; }
[JsonProperty("CustomerId")]
public int CustomerId { get; set; }
[JsonProperty("OrderId")]
public int OrderId { get; set; }
}
Deserialization then happens from your JSON string, as such:
StringReader stringReader = new StringReader(myJSONString);
JsonSerializer js = JsonSerializer.Create();
APIResponceContainer APIResponce = (APIResponceContainer)js.Deserialize(stringReader, typeof(APIResponceContainer));
Queries
As discussed in the comments, your data is unfortunately in terrible need of normalization. However, what I have inferred is that you would like to produce a flat structure, maintaining the "Few other Properties" and "key properties", for a combination of Order, PaymentDetail and CouponUsageDetail. You can use Linq for this, importantly I would recommend you choose yourself a "Primary Key". In other words, one property that can independently tie all the others together. In the example below, I have choose OrderID since it should be unique (?):
var flatSequence =
from order in APIResponce.Orders
join coupon in APIResponce.CouponUsageDetails on order.OrderId equals coupon.OrderId
join payment in APIResponce.PaymentDetails on order.OrderId equals payment.OrderId
select new
{
// Here extract all the properties you care about
OrderID = order.OrderId,
Customer = order.CustomerId,
Product = order.ProductId,
// All the "other Properties" ?
BankDetail = payment.PaymentOnlyProperty
};
Here I have extracted to var, however if you know the final flat structure you would like, of course determine a class of your own to receive the result.
Please comment if there is any questions.
You can use inheritance.
public class ResultCollection : Collection1
{
List<Collection2> Collection2s { get; set; }
List<Collection3> Collection3s { get; set; }
}
and then
var result = new ResultCollection {
PropId1 = Collection1.PropId1,
PropId2 = Collection1.PropId2,
...
Collection2s = Collection2,
Collection3s = Collection3
}
An automapper can be helpful here.
https://docs.automapper.org/en/stable/
I have solution, but i am not sure if it's ok for you. It depends on data format that you have on the begining.
solution:
class Program
{
static void Main(string[] args)
{
var collection1 = new Collection1() { PropId1 = 1, PropId2 = 2, PropId3 = 3 };
var list2 = new List<Collection2>()
{
new Collection2
{
PropId1 = 11,
PropId2 = 22,
PropId3 = 33
},
new Collection2
{
PropId1 = 22,
PropId2 = 33,
PropId3 = 44
}
};
var list3 = new List<Collection3>()
{
new Collection3
{
PropId1 = 111,
PropId2 = 222,
PropId3 = 333
},
new Collection3
{
PropId1 = 222,
PropId2 = 333,
PropId3 = 444
}
};
var result = new ResultCollection(collection1, list2, list3);
//or
var result2 = new ResultCollection(collection1) //but in this case you have to change your constructor
{
Collection2s = list2,
Collection3s = list3
};
Console.ReadLine();
}
}
public class Collection1
{
public int? PropId1 { get; set; }
public int? PropId2 { get; set; }
public int? PropId3 { get; set; }
}
public class Collection2
{
public int? PropId1 { get; set; }
public int? PropId2 { get; set; }
public int? PropId3 { get; set; }
}
public class Collection3
{
public int? PropId1 { get; set; }
public int? PropId2 { get; set; }
public int? PropId3 { get; set; }
}
public class ResultCollection : Collection1
{
public ResultCollection() { }
public ResultCollection(Collection1 collection, List<Collection2> list2, List<Collection3> list3)
{
foreach (PropertyInfo prop in collection.GetType().GetProperties())
{
PropertyInfo prop2 = collection.GetType().GetProperty(prop.Name);
if (prop2.CanWrite)
prop2.SetValue(this, prop.GetValue(collection, null), null);
}
Collection2s = list2;
Collection3s = list3;
}
public List<Collection2> Collection2s { get; set; }
public List<Collection3> Collection3s { get; set; }
}
But can you give an example of input data?

C# Copy List items to Object Arrays

I have a list created from a stored procedure using EF6.0
I have also created 3 classes
public class Resas
{
public string todo{ get; set; }
public string prop { get; set; }
public string Code { get; set; }
public string statusCode { get; set; }
public string checkin { get; set; }
public string checkout { get; set; }
public List<profiles> profiles { get; set; }
}
public class profiles
{
public string action { get; set; }
public string id { get; set; }
public string profileType { get; set; }
public string title { get; set; }
public string firstName { get; set; }
public string middleName { get; set; }
public string lastName { get; set; }
public List<emailAddresses> emailAdresses { get; set; }
}
public class emailAddresses
{
public string emailAddress { get; set; }
public string emailAddress2 { get; set; }
}
I am doing a for-loop in the list and I need to get certain columns and put it in the array (I will put two, to keep it simple)
myEntities db = new myEntities();
List<rev_Result> revList = new List<rev_Result>();
revList.Clear();
revList = db.rev().ToList();
for (int i = 0; i < revList.Count(); i++)
{
Resas resas = new Resas();
profiles[] profiles = new profiles[1];
resas.todo = revList[i].todo;
resas.profiles[0].lastName = revList[i].lastName;
}
I am not familiar with C# as you can see from the psedo-code above.
I cannot figure out how to feed the Resas with data and then its Profile with data and then move to the next Resas entry.
Any help appreciated.
That's fairly simple using Linq:
Resas resas = new Resas();
resas.profiles = revList
.Select(x => new profiles() { action = x.todo, lastName = x.lastName })
.ToList();
What's happening here is: You loop through every entry in revList and get your wanted data structure (that's what Select is doing). x refers to the current entry in the loop, while the stuff to the right side of the arrow is you 'output': a new instance of your profiles class with the members assigned accordingly. The result of all of this is then converted to a list (before ToList(), think of it as a recipe to create the list) and assigned to resas.profiles.
By the way, a word on conventions: Usually, in C#, you would give your classes a name that starts with a capital letter. Also, your profiles class seems to contain data of exactly one profile, so a better name might be Profile. This also makes your data structure more clear, since List<profiles> seems to be a list of lists of profiles - but that's not what it actually is, is it?
Furthermore, Members generally start with a capital letter as well, so instead of action, lastName, you'd have: Action and LastName.
You can try with Linq. This is the code that should solve your issue, but Resas class doesn't have action property:
List<Resas> ls = revList.Select(x => new Resas() {
action = x.todo,
profiles = new List<profiles>() {
new profiles { lastName = x.lastName }
}
).ToList();
If you need to use action property of inprofiles` class:
List<Resas> ls = revList.Select(x => new Resas() {
profiles = new List<profiles>() {
new profiles {
action = x.todo,
lastName = x.lastName
}
}
).ToList();

query IEnumerable array and select all x.profilenames

ok so this is maybe very simple but I just can not see where I am going wrong so any help is grateful
I have a model of this:
public IEnumerable<SelectedProfiels> profiles { get; set; }
public class SelectedProfiels
{
public int Identifer { get; set; }
public string ProfileName { get; set; }
}
and then I am trying to read from this
i am then building an array called selectedProfiles,
I then have this code
var pro = from s in selectedProfiles select s;
What im wanting to do is find all the ProfileName's and assigned them there on ViewBag.[i] or just there own string variable
I have a model collection like this
var userd = new User()
{
ID = i.FirstOrDefault(),
Type = t.FirstOrDefault(),
AuthorityLevel = a.FirstOrDefault(),
Language = l.FirstOrDefault(),
Profiles = pro,
};
public IEnumerable<SelectedProfiels> profiles { get; set; }
public class SelectedProfiels
{
public int Identifer { get; set; }
public string ProfileName { get; set; }
}
I then pass userd to a new page:
return RedirectToAction("test", "Account", userd);
public ActionResult test (Userm)
{
ViewBag.d = m.profiles;
return View();
}
any help please
So, given your linq query:
var pro = from s in
selectedProfiles
select s;
You can use Linq to generate an IEnumerable from pro, like:
var selectedProfiles = pro.Select(p => p.ProfileName).ToList();
Then, you can use selectedProfiles to populate the array you desire:
string[] newStrings = selectedProfiles.ToArray();
Instead of
select s
Just use
select s.ProfileName

EF adding duplicate records into lookup/reference table

I have 3 tables,
1. AttributeTypes (Columns: AttributeId (PK), AttributeName, ..)
2. Location (Columns: locationId (PK), LocationName, ...)
3. LocationAttributeType (Columns: locationId (FK), AttributeId (FK))
Whenever I am trying to insert new location record along with its attribute type from GUI, it should create new record for Table- Location and LocationAttributeType. But EF trying to add new record in Table- AttributeTypes as well, which is just used as reference table and should not add new/duplicate records in it. How can I prevent that?
here is my code,
The model which GUI sends is,
public class LocationDataModel
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Code { get; set; }
[DataMember]
public List<AttributeTypeDataModel> AssignedAttributes = new List<AttributeTypeDataModel>();
}
public class AttributeTypeDataModel
{
protected AttributeTypeDataModel() {}
public AttributeTypeDataModel(int id) { this.Id = id; }
public AttributeTypeDataModel(int id, string name)
: this(id)
{
this.Name = name;
}
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public virtual ICollection<LocationDataModel> Locations { get; set; }
}
The Entities created by EF are,
public partial class Location
{
public Location()
{
this.AttributeTypes = new List<AttributeType>();
}
public Location(int campusId, string code)
: this()
{
CampusId = campusId; Code = code;
}
public int Id { get; set; }
public int CampusId { get; set; }
public string Code { get; set; }
public virtual ICollection<AttributeType> AttributeTypes { get; set; }
}
public partial class AttributeType
{
public AttributeType()
{
this.Locations = new List<Location>();
}
public int AttributeTypeId { get; set; }
public string AttributeTypeName { get; set; }
public virtual ICollection<Location> Locations { get; set; }
}
I have below code to Add these new location to database,
private IEnumerable<TEntity> AddEntities<TModel, TEntity, TIdentityType>
(IEnumerable<TModel> models, Func<TModel, TIdentityType> primaryKey,
IGenericRepository<TEntity, TIdentityType> repository)
{
var results = new List<TEntity>();
foreach (var model in models)
{
var merged = _mapper.Map<TModel, TEntity>(model);
var entity = repository.Upsert(merged);
results.Add(entity);
}
repository.Save();
return results.AsEnumerable();
}
I am using following generic repository to do entity related operations
public TEntity Upsert(TEntity entity)
{
if (Equals(PrimaryKey.Invoke(entity), default(TId)))
{
// New entity
return Context.Set<TEntity>().Add(entity);
}
else
{
// Existing entity
Context.Entry(entity).State = EntityState.Modified;
return entity;
}
}
public void Save()
{
Context.SaveChanges();
}
Whats wrong I am doing here?
The DbSet<T>.Add() method attaches an entire object graph as added. You need to indicate to EF that the 'reference' entity is actually already present. There are two easy ways to do this:
Don't set the navigation property to an object. Instead, just set the corresponding foreign key property to the right value.
You need to ensure that you don't load multiple instances of the same entity into your object context. After creating the context, load the full list of AttributeType entities into the context and create a Dictionary<> to store them. When you want to add an attribute to a Location retrieve the appropriate attribute from the dictionary. Before calling SaveChanges() iterate through the dictionary and mark each AttributeType as unchanged. Something like this:
using (MyContext c = new MyContext())
{
c.AttributeTypes.Add(new AttributeType { AttributeTypeName = "Fish", AttributeTypeId = 1 });
c.AttributeTypes.Add(new AttributeType { AttributeTypeName = "Face", AttributeTypeId = 2 });
c.SaveChanges();
}
using (MyContext c = new MyContext())
{
Dictionary<int, AttributeType> dictionary = new Dictionary<int, AttributeType>();
foreach (var t in c.AttributeTypes)
{
dictionary[t.AttributeTypeId] = t;
}
Location l1 = new Location(1, "Location1") { AttributeTypes = { dictionary[1], dictionary[2] } };
Location l2 = new Location(2, "Location2") { AttributeTypes = { dictionary[1] } };
// Because the LocationType is already attached to the context, it doesn't get re-added.
c.Locations.Add(l1);
c.Locations.Add(l2);
c.SaveChanges();
}
In this specific case you are using a many-to-many relationship, with EF automatically handling the intermediate table. This means that you don't actually have the FK properties exposed in the model, and my first suggestion above won't work.
Therefore, you either need to use the second suggestion, which still ought to work, or you need to forgo the automatic handling of the intermediate table and instead create an entity for it. This would allow you to apply the first suggestion. You would have the following model:
public partial class Location
{
public Location()
{
this.AttributeTypes = new List<LocationAttribute>();
}
public Location(int campusId, string code)
: this()
{
CampusId = campusId; Code = code;
}
public int Id { get; set; }
public int CampusId { get; set; }
public string Code { get; set; }
public virtual ICollection<LocationAttribute> AttributeTypes { get; set; }
}
public partial class LocationAttribute
{
[ForeignKey("LocationId")]
public Location Location { get; set; }
public int LocationId { get; set; }
public int AttributeTypeId { get; set; }
}
public partial class AttributeType
{
public int AttributeTypeId { get; set; }
public string AttributeTypeName { get; set; }
}
With this approach you do lose functionality since you can't navigate from a Location to an AttributeType without making an intermediate lookup. If you really want to do that, you need to control the entity state explicitly instead. (Doing that is not so straightforward when you want to use a generic repository, which is why I've focused on this approach instead.)
Thank you all for your suggestions.
I have to get rid of my generic repository here to save my context changes and do it manually as below,
private IEnumerable<int> AddLocationEntities(IEnumerable<LocationDataModel> locations)
{
var results = new List<int>();
foreach (LocationDataModel l in locations)
{
var entity = _mapper.Map<LocationDataModel, Location>(l);//you can map manually also
var AttributeCode = l.AssignedAttributes.FirstOrDefault().AttributeTypeId;
using (MyContext c = new MyContext())
{
var attr = c.AttributeTypes.Where(a => a.Id == AttributeTypeId ).ToList();
entity.AttributeTypes = attr;
c.Locations.Add(entity);
c.SaveChanges();
var locid = entity.Id;
results.Add(locid);
}
}
return results;
}
In the else statement of yourUpsert you should add
context.TEntity.Attach(entity);

LINQ ExecuteQuery

I want to use ExecuteQuery() to get IEnumerable of type Entities.UserLevel. Following code works well.
using (CDataContext context = data.GetDataContext())
{
string q = "SELECT *FROM UserLevel";
IEnumerable<Entities.UserLevel> res = context.ExecuteQuery<Entities.UserLevel>(q);
}
public class UserLevel
{
public int userID { get; set; }
public int levID { get; set; }
}
But the problem is that I must use property names in UserLevel class same as in database, otherwise I do not get values. I wonder is there any way to get values irrespective of class/property name? For example, I want to use following UserLevel class instead of above:
public class UserLevel
{
public int UserIdentity { get; set; }
public int LevelIdentity { get; set; }
}
Sure just write.
var q = "SELECT userID as UserIdentity, levID as LevelIdentity FROM UserLevel";
var res = context.ExecuteQuery<UserLevelDTO>(q);
But why not use Linq instead? You could write:
var res = from u in context.UserLevel
select new UserLevelDTO()
{
UserIdentity = u.userID,
LevelIdentity = u.levID
};
You would need to create your own DTO class.
public class UserLevelDTO
{
public int UserIdentity { get; set; }
public int LevelIdentity { get; set; }
}

Categories

Resources