Pivot table in Linq - c#

I know you cannot cannot directly use the tSQL PIVOT function in Linq, but I cannot get the correct Linq syntax for what I feel is a simple transform (which I can do in straight tSQL)
I need to take this data set:
and pivot it into this:
Can someone please help me with the correct Linq syntax?
Here's how I can accomplish this in tSQL (both using PIVOT() and not)
Select piv.* from (
select custom_data_key, custom_data_value from dbo.kb_article_custom_data cd
inner join dbo.kb_article kb on cd.article_id = kb.article_id
where (custom_data_key='article_problem' or custom_data_key = 'article_cause' or custom_data_key='article_solution') and article_number='AKB26'
) d
pivot
(max(custom_data_value) for custom_data_key in([article_problem],[article_cause], [article_solution])) piv;
--WITHOUT USING PIVOT()
select
max(case when t.[custom_data_key]='article_problem' then t.[custom_data_value] end) as Article_problem,
max(case when t.[custom_data_key]='article_cause' then t.[custom_data_value] end) as Article_cause,
max(case when t.[custom_data_key]='article_solution' then t.[custom_data_value] end) as Article_solution
from(select custom_data_key, custom_data_value from dbo.kb_article_custom_data cd
inner join dbo.kb_article kb on cd.article_id = kb.article_id
where (custom_data_key='article_problem' or custom_data_key = 'article_cause' or custom_data_key='article_solution') and article_number='AKB26')t
This LINQ statement will get me the results in the first image above:
var query =
from a in custdata
join b in kbase on a.article_id equals b.article_id
where (a.custom_data_key == "article_problem" || a.custom_data_key == "article_cause" || a.custom_data_key == "article_solution") && b.article_number == id
select new { Key = a.custom_data_key, Value = a.custom_data_value };
Here's what is failing. I take the results of the Linq query above-
var q2 = from row in query
group row by "Value" into g
select new TO_Kbase
{
Problem= g.Where(c => c.Key =="article_problem" ).Select(c => c.Value).ToString(),
Cause = g.Where(c => c.Key =="article_cause").Select(c => c.Value).ToString(),
Solution = g.Where(c => c.Key =="article_solution").Select(c => c.Value).ToString()
};
foreach(var x in q2)
{
TO_Kbase kb = new TO_Kbase();
kb.Problem =x.Problem;
kb.Cause = x.Cause;
kb.Solution = x.Solution;
ta.Add(kb);
}
The output is this:
Problem: System.Data.Linq.SqlClient.Implementation.ObjectMaterializer`1+d__0`1[System.Data.SqlClient.SqlDataReader,System.String]
Cause: System.Data.Linq.SqlClient.Implementation.ObjectMaterializer`1+d__0`1[System.Data.SqlClient.SqlDataReader,System.String]
Solution: System.Data.Linq.SqlClient.Implementation.ObjectMaterializer`1+d__0`1[System.Data.SqlClient.SqlDataReader,System.String]

If I understood your data structure correctly, you could do something very similar to your "WITHOUT USING PIVOT()" T-SQL query:
// Test data.
var kb_article_custom_data = new CustomData[] {
new CustomData() { article_id = 1, custom_data_key = "article_problem", custom_data_value = "when you try ... 1"},
new CustomData() { article_id = 1, custom_data_key = "article_cause", custom_data_value = "the issues may occur ... 1"},
new CustomData() { article_id = 1, custom_data_key = "article_solution", custom_data_value = "1. Click start, then ... 1"},
new CustomData() { article_id = 2, custom_data_key = "article_problem", custom_data_value = "when you try ... 2"},
new CustomData() { article_id = 2, custom_data_key = "article_cause", custom_data_value = "the issues may occur ... 2"},
new CustomData() { article_id = 2, custom_data_key = "article_solution", custom_data_value = "1. Click start, then ... 2"},
new CustomData() { article_id = 3, custom_data_key = "article_problem", custom_data_value = "when you try ... 3"},
//new CustomData() { article_id = 3, custom_data_key = "article_cause", custom_data_value = "the issues may occur ... 3"},
new CustomData() { article_id = 3, custom_data_key = "article_solution", custom_data_value = "1. Click start, then ... 3"},
};
var kb_article = new Article[] {
new Article() { article_id = 1, article_title = "Title ... 1"},
new Article() { article_id = 2, article_title = "Title ... 2"},
new Article() { article_id = 3, article_title = "Title ... 3"},
};
// Query resembling your "without pivot" query.
var result =
from article in kb_article
join custom in kb_article_custom_data on article.article_id equals custom.article_id into ac
select new {
id = article.article_id,
title = article.article_title,
problem = ac.Where(x => x.custom_data_key == "article_problem").Select(x => x.custom_data_value).FirstOrDefault(x => x != null),
cause = ac.Where(x => x.custom_data_key == "article_cause").Select(x => x.custom_data_value).FirstOrDefault(x => x != null),
solution = ac.Where(x => x.custom_data_key == "article_solution").Select(x => x.custom_data_value).FirstOrDefault(x => x != null)
};
foreach (var r in result)
Console.WriteLine(r);
Which produces the following output:
{ id = 1, title = Title ... 1, problem = when you try ... 1, cause = the issues may occur ... 1, solution = 1. Click start, then ... 1 }
{ id = 2, title = Title ... 2, problem = when you try ... 2, cause = the issues may occur ... 2, solution = 1. Click start, then ... 2 }
{ id = 3, title = Title ... 3, problem = when you try ... 3, cause = , solution = 1. Click start, then ... 3 }
If you want to filter by a specific article_id, you need to add a where clause:
var id = 1;
var result =
from article in kb_article
where article.article_id == id
join custom in kb_article_custom_data on article.article_id equals custom.article_id into ac
select new
{
id = article.article_id,
title = article.article_title,
problem = ac.Where(x => x.custom_data_key == "article_problem").Select(x => x.custom_data_value).FirstOrDefault(x => x != null),
cause = ac.Where(x => x.custom_data_key == "article_cause").Select(x => x.custom_data_value).FirstOrDefault(x => x != null),
solution = ac.Where(x => x.custom_data_key == "article_solution").Select(x => x.custom_data_value).FirstOrDefault(x => x != null)
};
Producing the following output:
{ id = 1, title = Title ... 1, problem = when you try ... 1, cause = the issues may occur ... 1, solution = 1. Click start, then ... 1 }

Solution:
var query =
from a in custdata
join b in kbase on a.article_id equals b.article_id into ac
where (a.custom_data_key == "article_problem" || a.custom_data_key == "article_cause" || a.custom_data_key == "article_solution") && a.article_id == id
group a by new { a.article_id} into abc
select new
{
ID = abc.Key.article_id,
Cause = abc.Where(a =>a.custom_data_key == "article_cause").Select(a => a.custom_data_value).FirstOrDefault(x => x != null),
Problem = abc.Where(a => a.custom_data_key == "article_problem").Select(a => a.custom_data_value).FirstOrDefault(x => x != null),
Solution = abc.Where(a => a.custom_data_key == "article_solution").Select(a => a.custom_data_value).FirstOrDefault(x => x != null)
};

Related

Linq query to get list inside list

Below is the query,
List<int> groupIdList = {1, 2};
var clientGroupData = from cge in base.context.Set<ClientGroupEngagement>()
join cg in base.context.Set<ClientGroup>() on cge.ClientGroupID equals cg.ClientGroupID
join cgu in base.context.Set<ClientGroupUser>() on cg.ClientGroupID equals cgu.ClientGroupID
join eng in base.context.Set<Engagement>() on cge.EngagementID equals eng.EngagementID
join cdc in base.context.Set<CountryDataCenter>()
on new { eng.CountryID, eng.EngagementVersion } equals new { cdc.CountryID, cdc.EngagementVersion }
join dc in base.context.Set<DataCenter>()
on cdc.DataCenterID equals dc.DataCenterID
join dcuri in base.context.Set<DataCenterURI>()
on new { dc.DataCenterID, cdc.EngagementVersion } equals new { dcuri.DataCenterID, dcuri.EngagementVersion }
join uritype in base.context.Set<URIType>()
on dcuri.URITypeID equals uritype.URITypeID
where groupIdList.Contains(cgu.ClientGroupID)
&& cg.IsActive
&& cdc.IsActive
&& dc.IsActive
&& dcuri.IsActive
&& uritype.IsActive
&& (dcuri.URITypeID == (int)URITypeEnum.WebUri || dcuri.URITypeID == (int)URITypeEnum.AppUri)
select new ClientGroupUserEngagementModel
{
EngagementId = cge.EngagementID,
EngagementDescription = eng.EngagementDescription,
EngagementStatusId = eng.EngagementStatusID,
ClientGroupId = cg.ClientGroupID,
ClientGroupGuid = cg.ClientGroupGUID,
ClientGroupName = cg.ClientGroupName,
UserId = cgu.ClientUserID,
FirstName = cgu.FirstName,
LastName = cgu.LastName,
IsGroupUserActive = cgu.IsActive,
LocatorDataModel = new LocatorDataModel
{
DatacenterId = cdc.DataCenterID,
DatacenterName = dc.DataCenterName,
EngagementVersion = cdc.EngagementVersion,
Uri = dcuri.URI,
UriTypeId = dcuri.URITypeID
},
};
var result = await clientGroupData.ToListAsync();
I am expecting list of LocatorDataModel in the result set. EngagementId is key which will be unique.
Currently it is pulling *2 records due to condition
(dcuri.URITypeID == (int)URITypeEnum.WebUri || dcuri.URITypeID == (int)URITypeEnum.AppUri)
How can get result like
Engagementid 1 : dataurl1 dataurl2
Engagementid 2: dataurl3 dataurl4, dataurl5
etc.
Any help is appreciated.
I think below sample code can help you:
var list = new[]
{
new { Engagementid = 1, Dataurl = "dataurl1"},
new { Engagementid = 1, Dataurl = "dataurl2"},
new { Engagementid = 2, Dataurl = "dataurl3"},
new { Engagementid = 2, Dataurl = "dataurl4"},
new { Engagementid = 2, Dataurl = "dataurl5"}
};
var result =
list.GroupBy(g => g.Engagementid)
.Select(c => new {Engagementid = c.Key, Dataurls = string.Join(",", c.Select(x=> x.Dataurl).ToList())})
.ToList();
That result will be:
[0]: { Engagementid = 1, Dataurls = "dataurl1,dataurl2" }
[1]: { Engagementid = 2, Dataurls = "dataurl3,dataurl4,dataurl5" }

Conditional Where clause in LINQ

suppose i am showing data in grid and i have many textboxes for filter the data.
textbox for employee id. if employee id textbox is empty then no where clause will be added but if it is not empty then where clause will be added for that. the same way we can filter data if salary textbox has value or employee name textbox has value.
i try to compose a conditional LINQ query but got error. here is mine
var sName="";
var r = from t in TblFamilies
where 1 == 1
if(sName!="")
{
&& t.Name="Keith";
};
select new
{
t.ID,
t.ParentID,
t.Name,
t.CurDate
};
r.Dump();
Try this:-
First select the data:-
var r = from t in TblFamilie
select new
{
t.ID,
t.ParentID,
t.Name,
t.CurDate
};
Then you can filter based on condition:-
if (sName!="")
r = r.Where(x => x.Name == sName);
If you want to mix And operator and Or operator together, check PredicateBuilder out here: http://www.albahari.com/nutshell/predicatebuilder.aspx
You can simply write like:
// begin with true if you start with And operator.
var predicate = PredicateBuilder.True<TblFamilie>();
predicate = predicate.And(t => t.CureDate < DateTime.UtcNow.AddDays(-1));
// you can mix with Or operator too.
predicate = predicate.Or(t => t.Name.Contains("blah"));
var results = context.TblFamilie
.Where(predicate)
.Select(new
{
// your projection here...
});
// begin with false if you start with Or operator.
var predicate2 = PredicateBuilder.False<TblFamilie>();
predicate2 = predicate2.Or(t => t.CureDate < DateTime.UtcNow.AddDays(-1));
// you can mix with And operator too.
predicate2 = predicate2.And(t => t.Name.Contains("blah"));
var results = context.TblFamilie
.Where(predicate)
.Select(new
{
// your projection here...
});
// even nesting is possible
var inner = PredicateBuilder.False<TblFamilie>();
inner = inner.Or (p => p.Name.Contains("foo"));
inner = inner.Or (p => p.Name.Contains("bar"));
var outer = PredicateBuilder.True<TblFamilie>();
outer = outer.And (p => p.CureDate > DateTime.UtcNow.AddDays(-3));
outer = outer.And (p => p.CureDate < DateTime.UtcNow.AddDays(-1));
outer = outer.And (inner);
var results = context.TblFamilie
.Where(outer)
.Select(new
{
// your projection here...
});
Updated
Okay, lets assume you have a Family class, and you get 'Families' from some where. You can use PredicateBuilder like this:
// you have 4 families from DB, API or anywhere.
var failies = new List<Family>
{
new Family { Id = 1, ParentId = 1, Name = "foo", Birthday = new DateTime(1971, 1, 1) },
new Family { Id = 1, ParentId = 1, Name = "bar", Birthday = new DateTime(1982, 1, 1) },
new Family { Id = 1, ParentId = 1, Name = "foobar", Birthday = new DateTime(1993, 1, 1) },
new Family { Id = 1, ParentId = 1, Name = "fake", Birthday = new DateTime(2000, 1, 1) },
};
// make predicate!
// if a family's Birthday is before than 1980 'or' Name contains "ke".
var predicate = PredicateBuilder.True<Family>();
predicate = predicate.And(o => o.Birthday < new DateTime(1980, 1, 1));
predicate = predicate.Or(o => o.Name.Contains("ke"));
// you should make IQueryable in order to use PredicateBuilder.
var result = failies.AsQueryable()
.Where(predicate)
.Select(o => new
{
o.Id, o.Name, o.Birthday // only project what you want.
})
.ToList();
// now, result should contains "foo" and "fake".
foreach (var family in result)
{
Debug.WriteLine("Name: " + family.Name);
}
Updated2
You can copy & paste to LinqPad in order to test how it works. Before you run this in the LinqPad,
Download LinqKit.dll from above link.
Make sure press 'F4' > Add > Browse > select LinqKit.dll > Add LinqKit namespace in the 'Additional Namespace Imports' tab.
In the Query panel, choose Language to 'C# Statement(s)'
paste this and run.
// you have 4 strings from DB, API or anywhere.
var strings = new List<string>
{
"foo",
"bar",
"foobar",
"fake"
};
// make predicate!
// if a string contains "oo" or "ke"
var predicate = PredicateBuilder.True<string>();
predicate = predicate.And(o => o.Contains("oo"));
predicate = predicate.Or(o => o.Contains("ke"));
// you should make IQueryable in order to use PredicateBuilder.
var result = strings.AsQueryable()
.Where(predicate)
.ToList();
// now, result should contains "foo", "foobar" and "fake".
foreach (var stringResult in result)
{
Debug.WriteLine("Name: " + stringResult);
}

Cannot get DbSet.Find to work with Moq (Using the Entity-Framework)

For some reason this code keeps failing. Anyone that can tell me why:
var activeLoans = new List<ActiveLoan> {
new ActiveLoan{
ID = 1,
CaseType = "STL",
LoanCode = 0
},
new ActiveLoan{
ID = 2,
CaseType = "STL",
LoanCode = 0
},
new ActiveLoan{
ID = 3,
CaseType = "STL",
LoanCode = 0
}
}.AsQueryable();
var activeLoanMockSet = new Mock<DbSet<ActiveLoan>>();
activeLoanMockSet.As<IQueryable<ActiveLoan>>().Setup(m => m.Provider).Returns(activeLoans.Provider);
activeLoanMockSet.As<IQueryable<ActiveLoan>>().Setup(m => m.Expression).Returns(activeLoans.Expression);
activeLoanMockSet.As<IQueryable<ActiveLoan>>().Setup(m => m.ElementType).Returns(activeLoans.ElementType);
activeLoanMockSet.As<IQueryable<ActiveLoan>>().Setup(m => m.GetEnumerator()).Returns(activeLoans.GetEnumerator());
mockContext.Setup(c => c.ActiveLoans).Returns(activeLoanMockSet.Object);
// This is the line that fails
Assert.AreNotEqual(mockContext.Object.ActiveLoans.Find( 1 ), null);
When I say fail i mean that the unit test that this is a part of fails.
I think you need to also setup the IDbSet::Find.
activeLoanMockSet.Setup(m => m.Find(It.IsAny<object[]>()))
.Returns<object[]>(ids => activeLoans.FirstOrDefault(d => d.ID == (int)ids[0]));

Left join Entity framework 5.0.0 not using LINQ

I've created this with LINQ
var main = (from p in participants
from cc in participants
where cc.Id != p.Id
select new { Id1 = p.Id, Id2 = cc.Id });
I was able to recreate it to..
var main = participants.SelectMany(n => participants, (x, y) => new { Id1 = x.Id, Id2 = y.Id })
.Where(x => x.Id1 != x.Id2).ToList();
Same think I want to do with...
var test = (from m in main
join cr in db.ContactRelations on new { Id1 = m.Id1, Id2 = m.Id2 } equals new { Id1 = cr.ContactId, Id2 = cr.RelationContactId } into tt
from cr in tt.DefaultIfEmpty()
select new { m.Id1, m.Id2, found = cr == null ? false : true }).ToList();
So far, It's not left join but only join.. and that null values is what I need..
var test = main.Join(db.ContactRelations,
c => new { Id1 = c.Id1, Id2 = c.Id2 },
x => new { Id1 = x.ContactId, Id2 = x.RelationContactId },
(c, x) => new { c.Id1, c.Id2, found = x == null ? false : true }).ToList();
Is possible to implement DeafultIfEmpty() somehow ?
Thanks..

Select Single Element from Jagged Array

I'm working on a problem that's making my brain melt although I don't think it should be this hard. My example is long so I'll try to keep my question short!
I have an Array object that contains some elements that are also Arrays. For example:
customerAddresses = new customer_address[]
{
new // address #1
{
customer_id = 6676979,
customer_address_seq = 1,
customer_address_match_codes = new []
{
new
{
customer_address_seq = 1,
customer_id = 6676979,
customer_match_code_id = 5
}
}
},
new // address #2
{
customer_id = 6677070,
customer_address_seq = 1,
customer_address_match_codes = new []
{
new
{
customer_address_seq = 1,
customer_id = 6677070,
customer_match_code_id = 4
},
new
{
customer_address_seq = 1,
customer_id = 6677070,
customer_match_code_id = 5
},
new
{
customer_address_seq = 1,
customer_id = 6677070,
customer_match_code_id = 3
}
}
},
new // address #3
{
customer_id = 6677070,
customer_address_seq = 2,
customer_address_match_code = new []
{
new
{
customer_address_seq = 2,
customer_id = 6677070,
customer_match_code_id = 4
},
new
{
customer_address_seq = 2,
customer_id = 6677070,
customer_match_code_id = 5
}
}
}
};
As you can see, the Array contains a number of address records, with one record per combination of customer_id and customer_address_seq. What I'm trying to do is find the best matching customer_address according to the following rules:
There must be customer_match_code_id equal to 4 and there must be one equal to 5
If there is a customer_match_code_id equal to 3, then consider that customer_address a stronger match.
According to the above rules, the 2nd customer_address element is the "best match". However, the last bit of complexity in this problem is that there could be multiple "best matches". How I need to handle that situation is by taking the customer_address record with the minimum customer_id and minimum customer_address_seq.
I was thinking that using LINQ would be my best bet, but I'm not experienced enough with it, so I just keep spinning my wheels.
Had to make a change to your class so that you are actually assigning your one collection to something:
customer_address_match_codes = new customer_address_match_code[]
{
new
{
customer_address_seq = 1,
customer_id = 6676979,
customer_match_code_id = 5
}
}
And then here is the LINQ that I've tested and does what you specify:
var result = (from c in customerAddresses
let isMatch = c.customer_address_match_codes
.Where (cu => cu.customer_match_code_id == 4).Any () &&
c.customer_address_match_codes
.Where (cu => cu.customer_match_code_id == 5).Any ()
let betterMatch = isMatch && c.customer_address_match_codes
.Where (cu => cu.customer_match_code_id == 3).Any () ? 1 : 0
where isMatch == true
orderby betterMatch descending, c.customer_id, c.customer_address_seq
select c)
.FirstOrDefault ();
I've worked up an example using your data with anonymous types here: http://ideone.com/wyteM
Not tested and not the same names but this should get you going
customer cb = null;
customer[] cs = new customer[] {new customer()};
foreach (customer c in cs.OrderBy(x => x.id).ThenBy(y => y.seq))
{
if(c.addrs.Any(x => x.num == "5"))
{
if(c.addrs.Any(x => x.num == "3"))
{
if (cb == null) cb = c;
if (c.addrs.Any(x => x.num == "2"))
{
cb = c;
break;
}
}
}
}
This sounds like a job for LINQ
var bestMatch = (from address in DATA
where address.customer_address_match_code.Any(
x => x.customer_match_code_id == 4)
where address.customer_address_match_code.Any(
x => x.customer_match_code_id == 5)
select address).OrderBy(
x => x.customer_address_match_code.Where(
y => y.customer_match_code_id >= 3)
.OrderBy(y => y.customer_match_code_id)
.First()
.customer_match_code_id).FirstOrDefault();
My theory is this: Select addresses that have both a customer_match_code_id == 4 and a customer_match_code_id == 5. Then sort them by the the lowest customer_match_code_id they have that are at least 3, and then take the very first one. If there are a customer_match_code_id that equals 3 then that one is selected, if not, some else is selected. If nothing matches both 4 and 5 then null is returned.
Untested.
Seems quite straight forward in LINQ:
var query =
from ca in customerAddresses
where ca.customer_address_match_codes.Any(
mc => mc.customer_match_code_id == 4)
where ca.customer_address_match_codes.Any(
mc => mc.customer_match_code_id == 5)
orderby ca.customer_id
orderby ca.customer_address_seq
orderby ca.customer_address_match_codes.Any(
mc => mc.customer_match_code_id == 3) descending
select ca;
var result = query.Take(1);
How does that look?

Categories