How to do If statement in Linq Query - asp.net-mvc

I currently have a list that contains the following
CountryCode (string)
CountryStr (string)
RegionStr (string)
RegionID (int)
AreaStr (string)
AreaID (int)
This is a flattened set of linked data (so basically the results of a joined search that ive stored)
The MVC route will only pass one string which I then need to match up to the data at the right level in the heirachy.
So I'm trying to query the CountryStr then if it doesn't produce results the region then the area; but I need to do that bit of the query and for instance...
var datURL = (from xs in myList
//query 1
where xs.RegionStr == rarREF
select new
{
regionID = xs.RegionId,
CountryID = xs.CountryCd
}
//IF theres no results
where xs.AreaStr == rarREF
select new
{
AreaID = xs.AreaID
regionID = xs.RegionId,
CountryID = xs.CountryCd
}
).ToList();
The only way I see of doing this at the moment is running each query separately then checking which returned values and using that one. I'm hoping there's a cleverer, cleaner method.

It won't be very easy to read, but you could do this in a single pass using something like this:
var datURL = (from xs in myList
where xs.RegionStr == rarREF || xs.AreaStr == rarREF
select new
{
AreaID = (xs.AreaStr == rarRef ? xs.AreaID : default(int)),
RegionID = xs.RegionId,
CountryID = xs.CountryId
}
).ToList();
It might also be easier to read the query if it's rewritten slightly:
var datURL = (from xs in myList
let isArea = xs.AreaStr == rarREF
let isRegion = xs.RegionStr == rarREF
where isRegion || isArea
select new
{
AreaID = (isArea ? (int?)xs.AreaID : null),
RegionID = xs.RegionId,
CountryID = xs.CountryId
}
).ToList();
If we save the comparison result, we can reuse it later. I also added a cast to int? to show how you could use a nullable value instead of using 0 as your "no Area" value.

Aren't you looking for or operator? Does this not generate the results you want?
var datURL = (from xs in myList
where xs.RegionStr == rarREF || xs.AreaStr == rarREF
select new
{
AreaID = xs.AreaStr == rarREF ? xs.AreaID : default(int)
regionID = xs.RegionId,
CountryID = xs.CountryCd
}).ToList();

Related

Set format of cells produced by LoadFromCollection (epplus) using data annotation

I am using eeplus to create an excel spreadsheet, like this
using (var pck = new ExcelPackage())
{
var ws = pck.Workbook.Worksheets.Add("Customers");
ws.Cells["A1"].LoadFromCollection(customers, PrintHeaders: true);
var ms = new System.IO.MemoryStream();
pck.SaveAs(ms);
ms.WriteTo(Response.OutputStream);
}
The customer class has properties like
[DisplayName("Customer creation date")]
public DateTime Created { get; set; }
DisplayName seems to get honored, so the topmost line will read Customer creation date but the cell contents show up as 43257,41667.
What I would really like to have is cells that has the format 2018-04-05.
Can I do that will data annotations? I tried both
[DisplayName("Customer creation date")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime Created { get; set; }
and
[DisplayName("Customer creation date")]
[DataType(DataType.Date)]
public DateTime Created { get; set; }
but the cell contents remains the same.
No, EPPlus doesnot format your data according to data annotations.
It formats date as integers, so you should specify the column you wish to format as
ws.Column(colPosition+1).Style.Number.Format="yyyy-mm-dd";
You can find details here:
https://github.com/JanKallman/EPPlus/wiki/Formatting-and-styling
https://codereview.stackexchange.com/questions/139569/ensuring-specific-columns-in-an-excelworksheet-format-as-shortdate
EPPlus always changed column name while updating into excel based upon DisplayName Attribute else if there is no DisplayName Attribute is set, then it will Find "_" (underscore) character & replace it with " " (Space) Character in the column name, Due to which we cannot easily find PropertyInfo with help of column name to format the column as per our need.
Here is the simple & quickest solution to format column based upon indexing the PropertyInfo
PropertyInfo[] props = typeof(T).GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < props.Length; i++)
{
Type t = props[i].PropertyType;
if (t == typeof(DateTime) || t == typeof(DateTime?))
ws.Column(i + 1).Style.Numberformat.Format = "dd-MMM-yyyy HH:mm:ss";
else if (t == typeof(TimeSpan) || t == typeof(TimeSpan?))
ws.Column(i + 1).Style.Numberformat.Format = "HH:mm:ss";
}
I have another solution if you need to format columns based upon column names.
void ApplyDateTimeFormatting<T>(ExcelWorksheet ws, IEnumerable<T> data)
{
if (data.Count() == 0)
return;
Type type = data.First().GetType();
for (int c = 1; c <= toColumns; c++)
{
string column = ws.Cells[1, c].Text;
var t = type.GetPropertyWithDisplayName<T>(column).PropertyType;
if (t == typeof(DateTime) || t == typeof(DateTime?))
ws.Column(c).Style.Numberformat.Format = "dd-MMM-yyyy HH:mm:ss";
else if (t == typeof(TimeSpan) || t == typeof(TimeSpan?))
ws.Column(c).Style.Numberformat.Format = "HH:mm:ss";
}
}
PropertyInfo GetPropertyFromDisplayName(Type type, string DisplayName)
{
MemberInfo[] members = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var member in members)
{
DisplayNameAttribute displayNameAttribute = member
.GetCustomAttributes(typeof(DisplayNameAttribute), inherit: false)
.FirstOrDefault() as DisplayNameAttribute;
string text = ((displayNameAttribute == null) ? member.Name.Replace('_', ' ') :
displayNameAttribute.DisplayName);
if (text == DisplayName)
return type.GetProperty(member.Name);
}
return null;
}
I solved it as follows, so I just load the model and change as per my model if it is int or datetime
var li = typeof(Model).GetProperties().ToArray();
using (var package = new ExcelPackage(stream))
{
var workSheet = package.Workbook.Worksheets.Add("Sheet1");
var i = 0;
foreach (var c in li)
{
i++;
if(c.PropertyType.Name == typeof(DateTime).Name || c.PropertyType.Name == typeof(DateTime?).Name)
workSheet.Column(i).Style.Numberformat.Format = DateTimeFormatInfo.CurrentInfo.ShortDatePattern; ;
if (c.PropertyType.Name == typeof(int).Name || c.PropertyType.Name == typeof(int?).Name)
workSheet.Column(i).Style.Numberformat.Format = "0";
}
}

How can I get originalvalues from EF savechanges for complex primary key

i've implemented audit log for all my db objects but i'm running into an issue because of a special case.
i have code similar to the following.
private IEnumerable<CDMA_CHANGE_LOG> GetAuditRecords(DbEntityEntry ent, string userId, string primaryKeyId, bool updateFlag, object originalEntity)
{
List<CDMA_CHANGE_LOG> result = new List<CDMA_CHANGE_LOG>();
string changeId = Guid.NewGuid().ToString();
TableAttribute tableAttr = ent.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;
// Get table name (if it has a Table attribute, use that, otherwise get the pluralized name)
string entityName = tableAttr != null ? tableAttr.Name : ObjectContext.GetObjectType(ent.Entity.GetType()).Name;
var changeTime = DateTime.UtcNow;
if (ent.State == EntityState.Modified)
{
foreach (var prop in ent.OriginalValues.PropertyNames)
{
//we cant use this because getdatabasevalues will error out when there are 2 rows in the db for this primarykey (U,A)
//var originalValue = ent.GetDatabaseValues().GetValue<object>(prop) == null ? "" : ent.GetDatabaseValues().GetValue<object>(prop).ToString();
var currentValue = ent.CurrentValues[prop] == null ? "" : ent.CurrentValues[prop].ToString();
// For updates, we only want to capture the columns that actually changed
var primaryKey = GetPrimaryKeyValue(ent);
if (originalValue != currentValue)
{
result.Add(new CDMA_CHANGE_LOG()
{
USERID = userId,
DATECHANGED = changeTime,
EVENTTYPE = "M", // Modified
ENTITYNAME = entityName,
PRIMARYKEYVALUE = primaryKey.ToString(),
PROPERTYNAME = prop,
OLDVALUE = originalValue,
NEWVALUE = currentValue,
CHANGEID = changeId
});
}
}
}
return result;
}
now i cant use originalValue = ent.GetDatabaseValues().GetValue(prop) because the records have two fields as primary key so i thought i could query the original value manually from the controller and send as
object originalEntity
so i'm wondering how do i cast originalEntity to the current entity
var originalValues = Convert.ChangeType(originalEntity, ent.GetType());
does not work.
i'm finding it difficult to get. thanks for any help.

cannot set obj value from model return

public bool EditNew(News n)
{
News obj = new News();
obj = c.News.Where(p=>p.NewsID == n.NewsID).SingleOrDefault();
obj.NewsTitle = n.NewsTitle;
obj.IsActive = n.IsActive;
obj.IsHot = n.IsHot;
obj.NewsImage = n.NewsImage;
c.SaveChanges();
return true;
}
Not much information to go on, but assuming c is the DbContext object, it looks like you do not have a News object in your DB with the NewsId you are passing in to the EditNew method which results in the LINQ method SingleOrDefault() returning null

How to get number of days between two dates in asp.net MVC using Linq

I need to get the number of days between two dates from database using Linq Query, please help me how to do this. thanks
I tried the bellow code but it return me the result nul! please help
DateTime rightNow = DateTime.Now;
//int totalDays = (from d in db.vuExpiredProjectsAssigned
// where d.AssignedTo==obj.DepartmentID
// select (d.AssignedDate - d.DueDate).Days).FirstOrDefault();
//var numberdays = (from pd in db.vuExpiredProjectsAssigned
// where pd.AssignedTo == obj.DepartmentID
// select SqlFunctions.DateDiff("day", pd.AssignedDate, rightNow));
var result =(from dd in db.vuExpiredProjectsAssigned
where dd.AssignedTo==obj.DepartmentID
select new
{
days=SqlFunctions.DateDiff("Day",dd.DueDate,rightNow)
});
ViewBag.ndays = result;
Arithmetic with DateTime is not supported in Entity Framework. You have to use DbFunctions*. So, for the first part of your statement, something like:
var numberdays = ( from p in db.vuExpiredProjectsAssigned
where p.AssignedTo == obj.DepartmentID
select DbFunctions.DiffDays(p.AssignedDate,p.DueDate));
OR
var numberdays = ( from p in db.vuExpiredProjectsAssigned
where p.AssignedTo == obj.DepartmentID
select SqlFunctions.DateDiff("day", p.AssignedDate, p.DueDate));
For more reference please check below links.
SqlFunctions Class
DbFunctions Class
try with this
DbFunctions.DiffDays(dd.DueDate,rightNow).Value
Update
var rightNow= DateTime.Today.ToShortDateString();
public class democlass
{
public int count {get; set;}
}
and query like this
var result =(from dd in db.vuExpiredProjectsAssigned
where dd.AssignedTo==obj.DepartmentID
select new democlass()
{
count = DbFunctions.DiffDays(dd.DueDate,rightNow).Value
});
then check your result variable.
As long as you are not using the difference in days in the where clasuse, then it is better to do this calculation in your code instead of adding overhead to SQL, so change your code to:
public class democlass
{
public DateTime DueDate{get;set;}
public int count {get; set;}
}
var result =((from dd in db.vuExpiredProjectsAssigned
where dd.AssignedTo==obj.DepartmentID
select new democlass()
{
DueDate= dd.DueDate;
})).ToList();
result.ForEach(a=> {a.count = a.DueDate.Subtract(DateTime.Now).TotalDays});

Find Function with RichTextBox with Labels

What I'm trying to do is to make it where the user can type in a textbox and then click on a button and it will search the richtextbox for what they are looking for and if it found something it will change the label.
(Instances)
`
Button = btn_Search
Textbox = InputBox
RichTextBox = rtb
Label = Results`
Use this method, to find any Text inside your RichTextBox.
public int FindMyText(string searchText, int searchStart, int searchEnd)
{
// Initialize the return value to false by default.
int returnValue = -1;
// Ensure that a search string and a valid starting point are specified.
if (searchText.Length > 0 && searchStart >= 0)
{
// Ensure that a valid ending value is provided.
if (searchEnd > searchStart || searchEnd == -1)
{
// Obtain the location of the search string in richTextBox1.
int indexToText = richTextBox1.Find(searchText, searchStart, searchEnd, RichTextBoxFinds.MatchCase);
// Determine whether the text was found in richTextBox1.
if(indexToText >= 0)
{
// Return the index to the specified search text.
returnValue = indexToText;
}
}
}
return returnValue;
}
call this method like this:
var res= FindMyText("hello",0. richTextBox1.Text.Length);
now if res>-1, that means positive match, then you can set your labels i.e.
if(res>-1){
lbl1.Text = "hello found";
}
source here and here
Another method of searching text that is far more clean is as below,but first you need to add
System.Text.RegularExpressions namespace to your project;
private void SearchButton_Click(object sender, EventArgs e)
{
if (textBox1.TextLength >= 1)
{
string word = textBox1.Text;//The text you want to search.
Regex searchterm = new Regex(word);//A Regular Expression is most efficient way of working with text.
MatchCollection matches = searchterm.Matches(richTextBox1.Text);
if (matches.Count >= 1)
{
Results=matches.Count.ToString();//Your label to display match instances.
richTextBox1.SelectAll();
richTextBox1.SelectionBackColor = Color.White;
richTextBox1.DeselectAll();
foreach (Match match in matches)
{
richTextBox1.Select(match.Index, match.Length);
richTextBox1.SelectionBackColor = Color.Orange;
richTextBox1.DeselectAll();
}
}
}
}
This should do the job,furthermore if you want to specify additional search options,replace the line with Regex searchterm with anyone below,
Case Insensitive
Regex searchterm = new Regex(word,RegexOptions.IgnoreCase);
Whole Word Search
Regex searchterm = new Regex(#"\b"+word+"\b");
Case Insensitive and Whole Word Search
Regex searchterm = new Regex(#"\b"+word+"\b",RegexOptions.IgnoreCase);
and one more thing,Regex searches are case-sensitive by default.

Resources