Static Query Building with NEST - c#

I'm playing around with Elasticsearch and NEST.
I do have some trouble understanding the various classes and interfaces which can be used to create and build static queries.
Here's a simplified example of what I want to achieve:
using Nest;
using System;
using System.Text;
namespace NestTest
{
public class Product
{
public string Name { get; set; }
public int Price { get; set; }
}
public class ProductFilter
{
public string[] IncludeNames { get; set; }
public string[] ExcludeNames { get; set; }
public int MaxPrice { get; set; }
}
class Program
{
static void Main(string[] args)
{
var filter = new ProductFilter();
filter.MaxPrice = 100;
filter.IncludeNames = new[] { "Notebook", "Workstation" };
filter.ExcludeNames = new[] { "Router", "Modem" };
var query = CreateQueryFromFilter(filter);
var client = new ElasticClient();
// Test Serialization
var serialized = Encoding.UTF8.GetString(client.Serializer.Serialize(query));
Console.WriteLine(serialized);
// TODO: How to convert the IQuery to QueryContainer?
//client.Search<Product>(s => s.Query(q => query));
}
private static IQuery CreateQueryFromFilter(ProductFilter filter)
{
var baseBoolean = new BoolQueryDescriptor<Product>();
if (filter.IncludeNames != null && filter.IncludeNames.Length > 0)
{
foreach (var include in filter.IncludeNames)
{
// TODO: This overwrites the previous must
baseBoolean.Must(q => q.Term(t => t.Name, include));
}
}
if (filter.ExcludeNames != null && filter.ExcludeNames.Length > 0)
{
foreach (var exclude in filter.ExcludeNames)
{
// TODO: This overwrites the previous must
baseBoolean.MustNot(q => q.Term(t => t.Name, exclude));
}
}
if (filter.MaxPrice > 0)
{
// TODO: This overwrites the previous must
baseBoolean.Must(q => q.Range(r => r.LowerOrEquals(filter.MaxPrice).OnField(f => f.Price)));
}
return baseBoolean;
}
}
}
As you can see, I'd like to create some kind of query object (most likely BoolQuery) and then fill this object later on. I've added some TODOS in code where I have the actual problems. But in general, there are just too many possibilities (IQuery, QueryContainer, XXXQueryDescriptor, SearchDescriptor, SearchRequest) and I cannot figure out how to successfully "build" a query part by part.
Anybody who could enlighten me?

Combinding boolean queries is described in the documentation here:
http://nest.azurewebsites.net/nest/writing-queries.html
That page is slightly outdated and will be updated soon although most of it still applies I updated your CreateQueryFromFilter method to showcase the several ways you can formulate queries:
private static IQueryContainer CreateQueryFromFilter(ProductFilter filter)
{
QueryContainer queryContainer = null;
if (filter.IncludeNames != null && filter.IncludeNames.Length > 0)
{
foreach (var include in filter.IncludeNames)
{
//using object initializer syntax
queryContainer &= new TermQuery()
{
Field = Property.Path<Product>(p => p.Name),
Value = include
};
}
}
if (filter.ExcludeNames != null && filter.ExcludeNames.Length > 0)
{
foreach (var exclude in filter.ExcludeNames)
{
//using static Query<T> to dispatch fluent syntax
//note the ! support here to introduce a must_not clause
queryContainer &= !Query<Product>.Term(p => p.Name, exclude);
}
}
if (filter.MaxPrice > 0)
{
//fluent syntax through manually newing a descriptor
queryContainer &= new QueryDescriptor<Product>()
.Range(r => r.LowerOrEquals(filter.MaxPrice).OnField(f => f.Price)
);
}
return queryContainer;
}
Here's how you can pass that to a search operation:
static void Main(string[] args)
{
//using the object initializer syntax
client.Search<Product>(new SearchRequest()
{
Query = query
});
//using fluent syntax
client.Search<Product>(s => s.Query(query));
}

Related

Nested If to LINQ and Lambda

Net 6.0
Our company uses a metric layout that takes records that look like this
AAA|0000A|000AA|0001A
AAA|0000A|000AA|0002A
AAA|0000A|000AB|0001B
AAA|0000A|000AB|0002B
AAA|0000B|000BA|0001A
AAA|0000B|000BB|0001B
and transforms it into this
AAA|BLANK|BLANK|BLANK
AAA|0000A|BLANK|BLANK
AAA|0000B|BLANK|BLANK
AAA|0000A|000AA|BLANK
AAA|0000A|000AB|BLANK
AAA|0000B|000BA|BLANK
AAA|0000B|000BB|BLANK
AAA|0000A|000AA|0001A
AAA|0000A|000AA|0002A
AAA|0000A|000AA|0003A
AAA|0000A|000AA|0004A
AAA|0000A|000AB|0001B
AAA|0000A|000AB|0002B
AAA|0000B|000BA|0001A
AAA|0000B|000BB|0001B
I have a custom object
public class HierarchyStructure
{
public string Employer { get; set; }
public string Level1 { get; set; }
public string Level2 { get; set; }
public string Level3 { get; set; }
public string Level4 { get; set; }
public bool isRxlvl3 { get; set; }
public bool isExpired { get; set; }
public string lvl4SubType { get; set; }
public HierarchyStructure(string employer,
string? level1 = null,
string? level2 = null,
string? level3 = null,
string? level4 = null,
bool isRxlvl3 = false,
bool isExpired = false,
string? lvl4SubType = null)
{
this.Employer = employer;
this.Level1 = string.IsNullOrEmpty(level1) ? string.Empty : level1;
this.Level2 = string.IsNullOrEmpty(level2) ? string.Empty : level2;
this.Level3 = string.IsNullOrEmpty(level3) ? string.Empty : level3;
this.Level4 = string.IsNullOrEmpty(level4) ? string.Empty : level4;
this.isRxlvl3 = isRxlvl3;
this.isExpired = isExpired;
this.lvl4SubType = string.IsNullOrEmpty(lvl4SubType) ? string.Empty : lvl4SubType;
}
}
I am trying to populate from a static level into a formatted output list
private List<HierarchyStructure> AllLevels => _allLevels;
private List<HierarchyStructure> LocationLevels => _LocationLevels;
I developed methods for each level call, but I can't figure out how to merge them into one method that either calls itself recursively or dynamically selects the right values. I do not think it is possible to combine these due to the different comparisons and return values.
public bool GetDistinctEmployers()
{
var selectedLevels = (from levels in AllLevels
select new HierarchyStructure(levels.Employer));
foreach(HierarchyStructure level in selectedLevels) LocationLevels.Add(level);
return true;
}
public bool GetDistinctLevel1(string emp)
{
var selectedLevels = (from levels in AllLevels
where levels.Employer == emp
select new HierarchyStructure(levels.Employer,levels.Level1));
foreach(HierarchyStructure level in selectedLevels) LocationLevels.Add(level);
return true;
}
public bool GetDistinctLevel2(string lvl)
{
var selectedLevels = (from levels in AllLevels
where levels.Level1 == lvl
select new HierarchyStructure(levels.Employer,levels.Level1, levels.Level2));
foreach(HierarchyStructure level in selectedLevels) LocationLevels.Add(level);
return true;
}
public bool GetDistinctLevel3(string lvl)
{
var selectedLevels = (from levels in AllLevels
where levels.Level2 == lvl
select new HierarchyStructure(levels.Employer,levels.Level1, levels.Level2, levels.Level3));
foreach(HierarchyStructure level in selectedLevels) LocationLevels.Add(level);
return true;
}
public bool GetDistinctLevel4(string lvl)
{
var selectedLevels = (from levels in AllLevels
where levels.Level3 == lvl
select new HierarchyStructure(levels.Employer,levels.Level1, levels.Level2, levels.Level3, levels.Level4));
foreach(HierarchyStructure level in selectedLevels) LocationLevels.Add(level);
return true;
}
When I try and combine all these together, it ends up being soooo nested and I feel like there must be a better way to accomplish this. I am fairly certain this level of nesting will error my quality gate on my pipeline.
public void ProcessLevels()
{
if(GetDistinctEmployers())
{
foreach(string employer in AllLevels.Select(x => x.Employer))
{
if(GetDistinctLevel1(employer))
{
foreach(string level1 in AllLevels.Select(x => x.Level1))
{
if(GetDistinctLevel2(level1))
{
foreach(string level2 in AllLevels.Select(x => x.Level2))
{
if(GetDistinctLevel3(level2))
{
foreach(string level3 in AllLevels.Select(x => x.Level3))
{
if(GetDistinctLevel4(level3))
{
SaveData();
}
}
}
}
}
}
}
}
}
}
I am also getting another inline quality flag that my expressions in this block can be shortened using LINQ. I dont understand how my one line can be simplified.
...AllLevels.Select(x => x.Employer)
...AllLevels.Select(x => x.Level1)
... etc
A view of the data output I am trying to get.
Update: Thank you for the help offered - using the answers provided in this thread, I have coded my solution like this eliminating a lot of loops.
public void ProcessLevels()
{
var employers = AllLevels.Where(x=> !x.isExpired ).Select(x => x.Employer).Distinct();
var lvl1 = AllLevels.Where(x=> !x.isExpired ).Select(x => new {x.Employer, x.Level1 }).Distinct();
var lvl2 = AllLevels.Where(x=> !x.isExpired ).Select(x => new {x.Employer, x.Level1, x.Level2 }).Distinct();
var lvl3 = AllLevels.Where(x=> !x.isExpired ).Select(x => new {x.Employer, x.Level1, x.Level2, x.Level3 }).Distinct();
var lvl4 = AllLevels.Where(x=> !x.isExpired ).Select(x => new {x.Employer, x.Level1, x.Level2, x.Level3, x.Level4 }).Distinct();
foreach(var emp in employers) { LocationLevels.Add(new LevelHierarchyStructure(emp)); }
foreach(var lvl in lvl1) { LocationLevels.Add(new LevelHierarchyStructure(lvl.Employer, lvl.Level1)); }
foreach(var lvl in lvl2) { LocationLevels.Add(new LevelHierarchyStructure(lvl.Employer, lvl.Level1, lvl.Level2)); }
foreach(var lvl in lvl3) { LocationLevels.Add(new LevelHierarchyStructure(lvl.Employer, lvl.Level1, lvl.Level2, lvl.Level3)); }
foreach(var lvl in lvl4) { LocationLevels.Add(new LevelHierarchyStructure(lvl.Employer, lvl.Level1, lvl.Level2, lvl.Level3, lvl.Level4)); }
SaveData();
}
I assume someone will flag me for posting my update, but im posting it so ppl understand what i did and see my final solution
Perhaps you need something that first expands each source row into all of the possible levels, and runs those results through a .Distinct() and an .OrderBy().
Given:
AAA|0000A|000AA|0001A
AAA|0000A|000AA|0002A
AAA|0000A|000AB|0001B
AAA|0000A|000AB|0002B
AAA|0000B|000BA|0001A
AAA|0000B|000BB|0001B
The expanded data would be:
AAA
AAA|0000A
AAA|0000A|000AA
AAA|0000A|000AA|0001A
AAA
AAA|0000A
AAA|0000A|000AA
AAA|0000A|000AA|0002A
...
AAA
AAA|0000B
AAA|0000B|000BB
AAA|0000B|000BB|0001B
Then a .Distinct() and .OrderBy()
AAA
AAA|0000A
AAA|0000A|000AA
AAA|0000A|000AA|0001A
AAA|0000A|000AA|0002A
...
AAA|0000B
AAA|0000B|000BB
AAA|0000B|000BB|0001B
...
You would need a generating function to expand each source row into an enumerated list than can be fed into a .SelectMany(), and may also need to define custom comparators to be used by the .Distinct() and a .OrderBy() functions.
Something like:
private static IEnumerable<HierarchyStructure> HierarchyGenerator(this HierarchyStructure level)
{
yield return new HierarchyStructure(levels.Employer);
yield return new HierarchyStructure(levels.Employer,levels.Level1);
yield return new HierarchyStructure(levels.Employer,levels.Level1, levels.Level2);
yield return new HierarchyStructure(levels.Employer,levels.Level1, levels.Level2, levels.Level3);
yield return new HierarchyStructure(levels.Employer,levels.Level1, levels.Level2, levels.Level3, levels.Level4);
}
...
LocationLevels = AllLevels
.SelectMany(levels => levels.HierarchyGenerator())
.Distinct(...custom HierarchyStructure IEqualityComparer...)
.OrderBy(hier => hier, ...custom HierarchyStructure IComparer...)
.ToList();
There are other ways, such as generating distinct values are each level, unioning them all together, and then feeding them to the sort.
Something like:
var level0 = AllLevels
.Select(levels => newHierarchyStructure(levels.Employer))
.Distinct(IEqualityComparer...);
...
var level4 = AllLevels
.Select(levels => new HierarchyStructure(levels.Employer,levels.Level1, levels.Level2, levels.Level3, levels.Level4))
.Distinct(IEqualityComparer...);
LocationLevels = level0
.Union(level1)
.Union(level2)
.Union(level3)
.Union(level4)
.OrderBy(hier => hier, ...custom HierarchyStructure IComparer...)
.ToList();
There may be some performance tradeoffs in selecting where the distinct operations are applied. Using intermediate anonymous objects might also help, such as:
var level1 = AllLevels
.Select(levels => new {levels.Employer, levels.Level1})
.Distinct()
.Select(levels => new HierarchyStructure(levels.Employer,levels.Level1));
Here the .Distinct() uses the default comparator for the anonymous object, which compares each contained value.
The custom comparers can also be incorporated into the HierarchyStructure class by implementing the IComparable interface. The HierarchyGenerator() function could also be made a member function within the HierarchyStructure class.
(My apologies in advance for any syntax errors. This is untested. I'll update the above given any comments.)
This seems to be able to be solved quite easily with LINQ.
I'll start with this input:
string[] input = new[]
{
"AAA|0000A|000AA|0001A",
"AAA|0000A|000AA|0002A",
"AAA|0000A|000AB|0001B",
"AAA|0000A|000AB|0002B",
"AAA|0000B|000BA|0001A",
"AAA|0000B|000BB|0001B",
};
Now I can transform this into the output like this:
string[][] parts = input.Select(x => x.Split('|')).ToArray();
int max = parts.Max(p => p.Length);
string[][] expanded =
Enumerable
.Range(0, max)
.SelectMany(i => parts.Select(p => p.Take(i + 1).ToArray()).ToArray())
.DistinctBy(xs => String.Join("|", xs))
.OrderBy(xs => String.Join("|", xs))
.ToArray();
string[] output =
expanded
.Select(e => String.Join("|", e))
.ToArray();
That gives me:
AAA
AAA|0000A
AAA|0000A|000AA
AAA|0000A|000AA|0001A
AAA|0000A|000AA|0002A
AAA|0000A|000AB
AAA|0000A|000AB|0001B
AAA|0000A|000AB|0002B
AAA|0000B
AAA|0000B|000BA
AAA|0000B|000BA|0001A
AAA|0000B|000BB
AAA|0000B|000BB|0001B
If you just want to stop to fill a grid, just use expanded.
With this approach it doesn't matter how many levels deep the source data is.

EF6 Select all and Set One field

Is there a way to select all the properties in a collection and set one without verbosly remapping the entire property list?
return =
_input.Where(
w => w.MaterialNumber.Contains("foo")
).Select(s => new Material.list() { ID = s.ID, MaterialNumber = s.MaterialNumber, orColor = "#303030" }).ToList()
);
Looking for something simpler.
return =
_input.Where(
w => w.MaterialNumber.Contains("foo")
).Select(s => new Material.list() {s, new orColor = "#303030" }).ToList()
);
You mentioned EF 6 but you don't actually use it in the question. Also you mentioned mapping, but are you sure if it is required? If it is possible for other code to use same instance without remapping, just use any approach listed below to set property of the original object
using System.Linq;
namespace ConsoleApp1
{
class DTO
{
public string Color { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
var input = Enumerable.Repeat(new DTO(), 10);
// select statement
var selectOutput = input.Select(x =>
{
x.Color = "#303030";
return x;
});
// ForEach extension
var listOutput = input.ToList();
listOutput.ForEach(x => x.Color = "#303030");
// regular foreach
foreach (var inputItem in input)
{
inputItem.Color = "#303030";
}
}
}
}

Interleave an array of email addresses avoiding items with same domain to be consecutive

I'm looking for an efficient way of sorting an array of email addresses to avoid items with the same domain to be consecutive, in C#.
Email addresses inside the array are already distinct and all of them are lower case.
Example:
Given an array with the following entries:
john.doe#domain1.com
jane_doe#domain1.com
patricksmith#domain2.com
erick.brown#domain3.com
I would like to obtain something similar to the following:
john.doe#domain1.com
patricksmith#domain2.com
jane_doe#domain1.com
erick.brown#domain3.com
With the help of an extension method (stolen from https://stackoverflow.com/a/27533369/172769), you can go like this:
List<string> emails = new List<string>();
emails.Add("john.doe#domain1.com");
emails.Add("jane_doe#domain1.com");
emails.Add("patricksmith#domain2.com");
emails.Add("erick.brown#domain3.com");
var q = emails.GroupBy(m => m.Split('#')[1]).Select(g => new List<string>(g)).Interleave();
The Interleave method is defined as:
public static IEnumerable<T> Interleave<T>(this IEnumerable<IEnumerable<T>> source )
{
var queues = source.Select(x => new Queue<T>(x)).ToList();
while (queues.Any(x => x.Any())) {
foreach (var queue in queues.Where(x => x.Any())) {
yield return queue.Dequeue();
}
}
}
So basically, we create groups based on the domain part of the email adresses, project (or Select) each group into a List<string>, and then "Interleave" those lists.
I have tested against your sample data, but more thorough testing might be needed to find edge cases.
DotNetFiddle snippet
Cheers
This will distribute them semi-evenly and attempt to avoid matching domains next to each other (although in certain lists that may be impossible). This answer will use OOP and Linq.
DotNetFiddle.Net Example
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var seed = new List<string>()
{
"1#a.com",
"2#a.com",
"3#a.com",
"4#a.com",
"5#a.com",
"6#a.com",
"7#a.com",
"8#a.com",
"9#a.com",
"10#a.com",
"1#b.com",
"2#b.com",
"3#b.com",
"1#c.com",
"4#b.com",
"2#c.com",
"3#c.com",
"4#c.com"
};
var work = seed
// Create a list of EmailAddress objects
.Select(s => new EmailAddress(s)) // s.ToLowerCase() ?
// Group the list by Domain
.GroupBy(s => s.Domain)
// Create a List<EmailAddressGroup>
.Select(g => new EmailAddressGroup(g))
.ToList();
var currentDomain = string.Empty;
while(work.Count > 0)
{
// this list should not be the same domain we just used
var noDups = work.Where(w => w.Domain != currentDomain);
// if none exist we are done, or it can't be solved
if (noDups.Count() == 0)
{
break;
}
// find the first group with the most items
var workGroup = noDups.First(w => w.Count() == noDups.Max(g => g.Count()));
// get the email address and remove it from the group list
var workItem = workGroup.Remove();
// if the group is empty remove it from *work*
if (workGroup.Count() == 0)
{
work.Remove(workGroup);
Console.WriteLine("removed: " + workGroup.Domain);
}
Console.WriteLine(workItem.FullEmail);
// last domain looked at.
currentDomain = workItem.Domain;
}
Console.WriteLine("Cannot disperse email addresses affectively, left overs:");
foreach(var workGroup in work)
{
while(workGroup.Count() > 0)
{
var item = workGroup.Remove();
Console.WriteLine(item.FullEmail);
}
}
}
public class EmailAddress
{
public EmailAddress(string emailAddress)
{
// Additional Email Address Validation
var result = emailAddress.Split(new char[] {'#'}, StringSplitOptions.RemoveEmptyEntries)
.ToList();
if (result.Count() != 2)
{
new ArgumentException("emailAddress");
}
this.FullEmail = emailAddress;
this.Name = result[0];
this.Domain = result[1];
}
public string Name { get; private set; }
public string Domain { get; private set; }
public string FullEmail { get; private set; }
}
public class EmailAddressGroup
{
private List<EmailAddress> _emails;
public EmailAddressGroup(IEnumerable<EmailAddress> emails)
{
this._emails = emails.ToList();
this.Domain = emails.First().Domain;
}
public int Count()
{
return _emails.Count();
}
public string Domain { get; private set; }
public EmailAddress Remove()
{
var result = _emails.First();
_emails.Remove(result);
return result;
}
}
}
Output:
1#a.com
1#b.com
2#a.com
1#c.com
3#a.com
2#b.com
4#a.com
2#c.com
5#a.com
3#b.com
6#a.com
3#c.com
7#a.com
removed: b.com
4#b.com
8#a.com
removed: c.com
4#c.com
9#a.com
Cannot disperse email addresses affectively, left overs:
10#a.com
Something like this will spread them equally, but you will have the problems (=consecutive elements) at the end of the new list...
var list = new List<string>();
list.Add("john.doe#domain1.com");
list.Add("jane_doe#domain1.com");
list.Add("patricksmith#domain2.com");
list.Add("erick.brown#domain3.com");
var x = list.GroupBy(content => content.Split('#')[1]);
var newlist = new List<string>();
bool addedSomething=true;
int i = 0;
while (addedSomething) {
addedSomething = false;
foreach (var grp in x) {
if (grp.Count() > i) {
newlist.Add(grp.ElementAt(i));
addedSomething = true;
}
}
i++;
}
Edit: Added a high level description :)
What this code does is group each element by the domain, sort the groups by size in descending order (largest group first), project the elements of each group into a stack, and pop them off of each stack (always pop the next element off the largest stack with a different domain). If there is only a single stack left, then its contents are yielded.
This should make sure that all domains distributed as evenly as possible.
MaxBy extension method from: https://stackoverflow.com/a/31560586/969962
private IEnumerable<string> GetNonConsecutiveEmails(List<string> list)
{
var emailAddresses = list.Distinct().Select(email => new EmailAddress { Email = email, Domain = email.Split('#')[1]}).ToArray();
var groups = emailAddresses
.GroupBy(addr => addr.Domain)
.Select (group => new { Domain = group.Key, EmailAddresses = new Stack<EmailAddress>(group)})
.ToList();
EmailAddress lastEmail = null;
while(groups.Any(g => g.EmailAddresses.Any()))
{
// Try and pick from the largest stack.
var stack = groups
.Where(g => (g.EmailAddresses.Any()) && (lastEmail == null ? true : lastEmail.Domain != g.Domain))
.MaxBy(g => g.EmailAddresses.Count);
// Null check to account for only 1 stack being left.
// If so, pop the elements off the remaining stack.
lastEmail = (stack ?? groups.First(g => g.EmailAddresses.Any())).EmailAddresses.Pop();
yield return lastEmail.Email;
}
}
class EmailAddress
{
public string Domain;
public string Email;
}
public static class Extensions
{
public static T MaxBy<T,U>(this IEnumerable<T> data, Func<T,U> f) where U:IComparable
{
return data.Aggregate((i1, i2) => f(i1).CompareTo(f(i2))>0 ? i1 : i2);
}
}
What I am trying to do here is to sort them first.
Then I re-arrange from a different end. I'm sure there're more efficient ways to do this but this is one easy way to do it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
String[] emails = { "john.doe#domain1.com", "jane_doe#domain1.com", "patricksmith#domain2.com", "erick.brown#domain3.com" };
var result = process(emails);
}
static String[] process(String[] emails)
{
String[] result = new String[emails.Length];
var comparer = new DomainComparer();
Array.Sort(emails, comparer);
for (int i = 0, j = emails.Length - 1, k = 0; i < j; i++, j--, k += 2)
{
if (i == j)
result[k] = emails[i];
else
{
result[k] = emails[i];
result[k + 1] = emails[j];
}
}
return result;
}
}
public class DomainComparer : IComparer<string>
{
public int Compare(string left, string right)
{
int at_pos = left.IndexOf('#');
var left_domain = left.Substring(at_pos, left.Length - at_pos);
at_pos = right.IndexOf('#');
var right_domain = right.Substring(at_pos, right.Length - at_pos);
return String.Compare(left_domain, right_domain);
}
}
}

Entity framework error as"New transaction is not allowed because there are other threads running in the session

We are using entity framework codefirst approach
I am new to entity framework and I am facing error while trying to do "New transaction is not allowed because there are other threads running in the session.
public class DatabaseBackup : IDataBackup
{
private readonly IMonarchDbContext m_db;
public DatabaseBackup(IMonarchDbContext podb)
{
if (podb == null)
throw new ArgumentNullException("podb");
m_db = podb;
}
public DBBackupHistory GetLatestBackupHistory(DBBackupFrequency backupFrequency = DBBackupFrequency.Periodic)
{
DBBackupHistory result = null;
// get the backup history of the given backuptype and populate the objects
var configId = m_db.DBBackupConfigurations.Where(c => c.ScheduleType == (int)backupFrequency && c.BackupStatus == 1).Distinct().Select(c => c.ConfigurationId).DefaultIfEmpty(-1).First();
if (configId > 0)
{
result = m_db.DBBackupHistorys.Where(b => b.Status == 1 && b.ConfigurationId == configId).OrderByDescending(lb => lb.BackupDatetime).FirstOrDefault();
}
return result;
}
public IEnumerable<DBBackupConfiguration> GetAllConfiguration()
{
var result = m_db.DBBackupConfigurations.Where(c => c.BackupStatus == 1).OrderByDescending(c => c.ConfigurationId);
return result;
}
public void Backup(DBBackupConfiguration config, int fileIndex)
{
Console.WriteLine("Running DB Backup type {0} to device {1}", (DBBackupType)config.BackupType, fileIndex);
m_db.StoredProc.SPBackup(config, fileIndex);
}
I am calling the below methods in another class as follows
private readonly IDataBackup m_dataBackup;
public int PerformBackup(int defaultPollIntervalInMinutes = 15)
{
// polling interval in Minutes
int pollInterval = defaultPollIntervalInMinutes;
int fileIndex = getCurrentDumpFileIndex();
// check for the backup configuration
var configurations = m_dataBackup.GetAllConfiguration();
foreach (var config in configurations)
{
var lastBackup = m_dataBackup.GetLatestBackupHistory(DBBackupFrequency.Weekly);
if (lastBackup == null)
{
m_dataBackup.Backup(config, fileIndex + 1);
break;
}
Here is the Db Context class is as below
public class MonarchDbContext:DbContext,IMonarchDbContext
{
private IStoredProcedure m_storedProc;
private static object m_dbIntializerSet;
public MonarchDbContext(string nameOrConnectionString)
: base( nameOrConnectionString )
{
//-- Set the DB initializer only once.
System.Threading.LazyInitializer.EnsureInitialized( ref m_dbIntializerSet,()=>{
Database.SetInitializer<MonarchDbContext>(null);
//-- Give debug builds a chance to overwrite the above.
_SetInitializerForDebugBuilds();
return new object();
});
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
var csb = new SqlConnectionStringBuilder( this.Database.Connection.ConnectionString );
csb.MultipleActiveResultSets = true;
this.Database.Connection.ConnectionString = csb.ToString();
var objectContext = ( this as IObjectContextAdapter ).ObjectContext;
objectContext.CommandTimeout = 3600;
}
#region Public "Tables"
public IDbSet<DBBackupConfiguration> DBBackupConfigurations { get; set; }
public IDbSet<DBBackupHistory> DBBackupHistorys { get; set; }
public IStoredProcedure StoredProc
{
get
{
return System.Threading.LazyInitializer.EnsureInitialized(ref m_storedProc, () => new BackupStoredProc(this.Database));
}
}
#endregion
please let me know how can i solve the issue.
I found the issue
I need to add toList() at the end of the Linq code and it just worked for me.
public IEnumerable<DBBackupConfiguration> GetAllConfiguration()
{
var result = m_db.DBBackupConfigurations.Where(c => c.BackupStatus == 1).OrderByDescending(c => c.ConfigurationId).ToList();
return result;
}
Just add the List to Ienumerbale types

Replace a collection item using Linq

How do I find and replace a property using Linq in this specific scenario below:
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
//TODO: Just copying values... Find out how to find the index and replace the value
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
Thanks for helping out in advance.
Do not use LINQ because it will not improve the code because LINQ is designed to query collection and not to modify them. I suggest the following.
// Just realized that Array.IndexOf() is a static method unlike
// List.IndexOf() that is an instance method.
Int32 index = Array.IndexOf(this.Properties, name);
if (index != -1)
{
this.Properties[index] = value;
}
else
{
throw new ArgumentOutOfRangeException();
}
Why are Array.Sort() and Array.IndexOf() methods static?
Further I suggest not to use an array. Consider using IDictionary<String, Property>. This simplifies the code to the following.
this.Properties[name] = value;
Note that neither solution is thread safe.
An ad hoc LINQ solution - you see, you should not use it because the whole array will be replaced with a new one.
this.Properties = Enumerable.Union(
this.Properties.Where(p => p.Name != name),
Enumerable.Repeat(value, 1)).
ToArray();
[note: this answer was due to a misunderstanding of the question - see the comments on this answer. Apparently, I'm a little dense :(]
Is your 'Property' a class or a struct?
This test passes for me:
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
[TestMethod]
public void TestMethod1()
{
var pb = new PropertyBag() { Properties = new Property[] { new Property { Name = "X", Value = "Y" } } };
Assert.AreEqual("Y", pb["X"].Value);
pb["X"] = new Property { Name = "X", Value = "Z" };
Assert.AreEqual("Z", pb["X"].Value);
}
I have to wonder why the getter returns a 'Property' instead of whatever datatype .Value, but I'm still curious why you're seeing a different result than what I am.

Categories