Importing data with LinqToExcel - Readonly properties - c#

I'm trying to import data from excel using LinqToExcel. I have few readonly properties in the model class. When I try to map them with the excel columns, they fail with following error. Also when I drop those columns from excel, it works fine without values.
Method 'Total' not found.
Model:Budget
[Required]
[Display(Name = "Room Type")]
public int RoomTypeID { get; set; }
[ForeignKey("RoomTypeID")]
public virtual RoomType RoomType { get; set; }
public decimal Pair { get; set; }
[ExcelColumn("Cost per Room*")]
public decimal CostPerRoom { get; set; }
[NotMapped]
[ExcelColumn("Total")]
[Display(Name = "Total")]
public decimal Total
{
get
{
if (this.RoomType != null)
{
return this.CostPerRoom * this.RoomType.RoomTypeQty * this.Pair;
}
else
{
return 0;
}
}
}
Budget Controller:
public ActionResult ReadFromExcel()
{
var file = Request.Files[0];
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/Uploads/"), fileName);
file.SaveAs(path);
var excel = new ExcelQueryFactory(path);
excel.DatabaseEngine = DatabaseEngine.Ace;
excel.TrimSpaces = LinqToExcel.Query.TrimSpacesType.Both;
var budgets = from c in excel.Worksheet<Budget>("WorksheeName") select c;
foreach (var item in budgets) // This is where it generates the error.
{
}
}
How do I overcome this?

Related

Allowing a user to select column headers to import

I'm using LINQtoCSV within a program that allows the user to import an order from a CSV file. I have all the code working however, if the CSV file doesn't have the exact column headers then it doesn't work.
Below is my class that LINQtoCSV reads into -
public class orderProduct
{
public orderProduct() { }
public string product { get; set; }
public string price { get; set; }
public string orderQty { get; set; }
public string value { get; set; }
public string calculateValue()
{
return (Convert.ToDouble(price) * Convert.ToDouble(orderQty)).ToString();
}
}
If the CSV file doesn't have the exact headers it won't work. The data I actually only need is the first 4 strings.
Below is my function that actually reads in the data.
private void csvParse()
{
// order.Clear();
string fileName = txt_filePath.Text.ToString().Trim();
try
{
CsvContext cc = new CsvContext();
CsvFileDescription inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
IEnumerable<orderProduct> fromCSV = cc.Read<orderProduct>(fileName, inputFileDescription);
foreach (var d in fromCSV)
{
MessageBox.Show($#"Product:{d.product},Quantity:""{d.orderQty}"",Price:""{d.price}""");
orderReturn.Add(d);
}
this.DialogResult = DialogResult.Yes;
this.Close();
}
catch (Exception ex)
{
if (ex.ToString().Contains("being used by another process"))
{
MessageBox.Show("Error: Please close the file in Excel and try again");
}
else
{
MessageBox.Show(ex.ToString());
}
}
}
I want the user to be able to just pass in a file and then select the relevant columns which relate to the corresponding values and then read in the data ignoring any columns that haven't been selected.
Hope this all makes sense, is something like this possible within LINQtoCSV
You have to add IgnoreUnknownColumns = true to your CsvFileDescription
CSV:
product,price,someColumn,orderQty,value,otherColumn
my product,$123,xx,2,$246,aa
my other product,$10,yy,3,$30,bb
Working code (I modified your code a little bit, to run it in a console)
using System;
using System.Collections.Generic;
using LINQtoCSV;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
csvParse();
Console.ReadLine();
}
private static void csvParse()
{
string fileName = "../../../test.csv"; // provide a valid path to the file
CsvContext cc = new CsvContext();
CsvFileDescription inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true,
IgnoreUnknownColumns = true // add this line
};
IEnumerable<orderProduct> fromCSV = cc.Read<orderProduct>(fileName, inputFileDescription);
foreach (var d in fromCSV)
{
Console.WriteLine($#"Product:{d.product},Quantity:""{d.orderQty}"",Price:""{d.price}""");
}
}
}
public class orderProduct
{
public orderProduct() { }
public string product { get; set; }
public string price { get; set; }
public string orderQty { get; set; }
public string value { get; set; }
public string calculateValue()
{
return (Convert.ToDouble(price) * Convert.ToDouble(orderQty)).ToString();
}
}
}
Output:
Product:my product,Quantity:"2",Price:"$123"
Product:my other product,Quantity:"3",Price:"$10"
If your properties have different names than CSV columns, you should use CsvColumn attribute:
public class OrderProduct
{
[CsvColumn(Name = "product")]
public string Product { get; set; }
[CsvColumn(Name = "price")]
public string Price { get; set; }
[CsvColumn(Name = "orderQty")]
public string OrderQuantity { get; set; }
public string Value { get; set; }
public string calculateValue()
{
return (Convert.ToDouble(Price) * Convert.ToDouble(OrderQuantity)).ToString();
}
}
Or if you prefer mapping columns by their indices:
public class OrderProduct
{
[CsvColumn(FieldIndex = 0)]
public string Product { get; set; }
[CsvColumn(FieldIndex = 1)]
public string Price { get; set; }
[CsvColumn(FieldIndex = 2)]
public string OrderQuantity { get; set; }
public string Value { get; set; }
public string calculateValue()
{
return (Convert.ToDouble(Price) * Convert.ToDouble(OrderQuantity)).ToString();
}
}
If you have to specify the columns on the fly, the only way seems to be to read raw data and process it yourself (the solution is based on this article):
internal class DataRow : List<DataRowItem>, IDataRow
{
}
...
int productColumnIndex = 0; // your users will provide it
var fromCSV = cc.Read<DataRow>(fileName);
foreach (var row in fromCSV)
{
var orderProduct = new OrderProduct
{
Product = row[productColumnIndex].Value,
};
Console.WriteLine(orderProduct.Product);
}

Import the values stored in the database from the controller

[HttpGet]
public ActionResult Create()
{
Articles article = new Articles();
return View(article);
}
[HttpPost]
public ActionResult Create(Articles article)
{
try
{
article.Family = article.ArticleIDX; //그룹번호
article.Parent = 0; //순서
article.Depth = 1; //그룹내 최상위 글로부터 매겨지는 순서
article.Indent = 0; //들여쓰기
article.ModifyDate = DateTime.Now;
article.ModifyMemberID = User.Identity.Name;
db.Articles.Add(article);
db.SaveChanges();
if (Request.Files.Count > 0)
{
var attachFile = Request.Files[0];
if (attachFile != null && attachFile.ContentLength > 0)
{
var fileName = Path.GetFileName(attachFile.FileName);
var path = Path.Combine(Server.MapPath("~/Upload/"), fileName);
attachFile.SaveAs(path);
ArticleFiles file = new ArticleFiles();
file.ArticleIDX = article.ArticleIDX;
file.FilePath = "/Upload/";
file.FileName = fileName;
file.FileFormat = Path.GetExtension(attachFile.FileName);
file.FileSize = attachFile.ContentLength;
file.UploadDate = DateTime.Now;
db.ArticleFiles.Add(file);
db.SaveChanges();
}
}
ViewBag.Result = "OK";
}
catch (Exception ex)
{
Debug.WriteLine("Board");
Debug.WriteLine(ex.ToString());
ViewBag.Result = "FAIL";
}
return View(article);
//return RedirectToAction("ArticleList");
}
[HttpGet]
public ActionResult ReplyCreate(int aidx)
{
Articles articleReply = new Articles();
return View(articleReply);
}
[HttpPost]
public ActionResult ReplyCreate(Articles replyArticles)
{
Articles articles = new Articles();
try
{
//부모글 불러오기(글번호로 불러오기)
Articles note = db.Articles.Find(articles.ArticleIDX);
//Family는 원글의 번호
replyArticles.Family = replyArticles.ArticleIDX;
//Parent 순서
//Depth 는 답글의 글 번호
//Indent 들여쓰기
replyArticles.ModifyDate = DateTime.Now;
replyArticles.ModifyMemberID = User.Identity.Name;
db.Articles.Add(replyArticles);
db.SaveChanges();
ViewBag.Result = "OK";
}
catch (Exception ex)
{
ViewBag.Result = "FAIL";
}
return View(replyArticles);
}
public partial class Articles
{
[Key]
public int ArticleIDX { get; set; }
public int? Family { get; set; }
public int? Depth { get; set; }
public int? Indent { get; set; }
public int? Parent { get; set; }
[StringLength(200)]
public string Title { get; set; }
[Column(TypeName = "text")]
public string Contents { get; set; }
[StringLength(50)]
public string Category { get; set; }
[StringLength(20)]
public string ModifyMemberID { get; set; }
public DateTime? ModifyDate { get; set; }
public virtual Members Members { get; set; }
}
The above code is the code I implemented.
Articles created using the create method are stored in the database.
What do you do when you try to recall a post stored in the database with ReplyCreate?
The null value is often entered into the model.
I try to find it using the primary key, but the primary key also has a null value.
Articles note = db.Articles.Find(articles.ArticleIDX);
does not work because articles is an empty object, due to the line
Articles articles = new Articles();
just above. You never set any of the fields in this object, including the ArticleIDX, before calling the Find method.
I think you probably intended to search using the value passed in to the controller? In that case it would need to be
Articles note = db.Articles.Find(replyArticles.ArticleIDX);
because replyArticles is the variable which was received from the browser in the request. I assume this contains a value in the ArticleIDX field.
Having said that, I don't know what the purpose of this line of code is, because you never use the note object in any of the following code, so I don't know why you needed to find it.

How to update a record in database in Entity Framework?

I'm using c# mvc with EF and i'm building a website. When i'm updating a table with new values it gives the following error.
{"Violation of PRIMARY KEY constraint 'PK_Table_1_1'. Cannot insert duplicate key in object 'dbo.User'. The duplicate key value is (shan#gmail.com).\r\nThe statement has been terminated."}
Here is my design of the table.
Here is my controller file
[HttpPost]
public ActionResult Manage(ManageViewModel manageviewmodel)
{
TheFoodyContext db = new TheFoodyContext();
string UserEmail = Session["UserEmail"].ToString();
User user_to_update = db.Users.Find(UserEmail);
if (ModelState.IsValid)
{
try
{
HttpPostedFileBase photo = Request.Files["photo"];
if (photo != null && photo.ContentLength > 0)
{
var path = "";
var fileName = Path.GetFileName(photo.FileName);
var extension = Path.GetExtension(photo.FileName);
var allowedExtensions = new[] {".Jpg", ".png", ".jpg", "jpeg"};
if (allowedExtensions.Contains(extension))
{
string name = Path.GetFileNameWithoutExtension(fileName);
string myfile = name + "_" + UserEmail + extension;
path= Path.Combine(Server.MapPath("~/Img"), myfile);
photo.SaveAs(path);
user_to_update.photo = myfile;
}
else
{
ViewBag.message = "Please choose only Image file";
}
user_to_update.email = UserEmail;
user_to_update.fname = manageviewmodel.FirstName;
user_to_update.lname = manageviewmodel.LastName;
user_to_update.phone = manageviewmodel.Phone;
user_to_update.address = manageviewmodel.Address;
user_to_update.city = manageviewmodel.City;
user_to_update.postcode = Convert.ToDecimal(manageviewmodel.PostCode);
user_to_update.district = manageviewmodel.District;
user_to_update.user_type = manageviewmodel.UserType;
user_to_update.status = manageviewmodel.Status;
user_to_update.photo = path;
db.Users.Add(user_to_update);
db.SaveChanges();
Session["UserEmail"] = UserEmail;
Session["FirstName"] = manageviewmodel.FirstName;
Session["LastName"] = manageviewmodel.LastName;
Session["Address"] = manageviewmodel.Address;
Session["City"] = manageviewmodel.City;
Session["PostCode"] = manageviewmodel.PostCode;
Session["District"] = manageviewmodel.District;
Session["UserType"] = manageviewmodel.UserType;
Session["Status"] = manageviewmodel.Status;
Session["Phone"] = manageviewmodel.Phone;
return RedirectToAction("Manage");
}
}
catch (Exception ex)
{
return View(ex.Message);
}
return View(manageviewmodel);
}
return View(manageviewmodel);
}
Here is my Model file
public class ManageViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string photo { get; set; }
public string Address { get; set; }
public string City { get; set; }
public int PostCode { get; set; }
public string District { get; set; }
public string UserType { get; set; }
public string Status { get; set; }
}
You don't need to add the user again (it already exists and EF tracks changes), simply call SaveChanges and you are done.
Just remove this line:
db.Users.Add(user_to_update);
and it should work (unless there are more errors).

Creating a progress bar for SQL query (using ADO.NET)

I'm trying to make a progress bar for my SQL query but don't know where to start. I have tried implementing the backgroundworker but I can't seem to get it to work.
I'm using VS 2010 .NET 4.0
Treinen trein = listboxVoertuigNr.SelectedItem as Treinen;
List<string> treinenIds = new List<string>();
foreach (var item in listboxVoertuigNr.SelectedItems)
{
treinenIds.Add(item.ToString());
}
fouten = eventsEntities.Foutens;
category = eventsEntities.Category_MMAP;
IEnumerable<dynamic> FoutenResultaat = new List<dynamic>();
FoutenResultaat =
(from x in treinen
join fout in fouten
on x.TreinId equals fout.TreinId
where dateStart <= fout.Datum && dateEnd >= fout.Datum
&& treinenIds.Contains(fout.Treinen.Name)
&& fout.Omschrijving.Contains(textboxFilterOmschrijving.Text)
&& fout.FoutCode.Contains(textboxFilterFout.Text)
&& fout.Module.Contains(textboxFilterModule.Text)
orderby fout.Datum descending, fout.Time descending
join cat in category
on fout.FoutCode equals cat.Foutcode
select new
{
Datum = fout.Datum,
Time = fout.Time,
FoutCode = fout.FoutCode,
Omschrijving = fout.Omschrijving,
Module = fout.Module.ToUpper(),
FoutId = fout.FoutId,
Name = x.Name,
Category = cat.Cat_MMAP
});
GetGraad(FoutenResultaat);
In GetGraad() the read data from the SQL database gets placed in a list. This is a custom list because it also gets populated with values from a excel file.
private void GetGraad(IEnumerable<dynamic> mijnResultaten)
{
foreach (dynamic item in mijnResultaten)
{
string graad = "";
string Toolbox = "";
if (item.Module.Contains("_"))
modNaam = item.Module.Split('_');
else if (item.Module.Contains(" "))
modNaam = item.Module.Split(' ');
string compare = modNaam[0].Substring(0, modNaam[0].Length - 1).ToString();
if (compare == "MPU")
{
var index = Properties.Settings.Default.listFoutcodeMPU.FindIndex(a => a == item.FoutCode);
if (index == -1)
graad = "";
else
{
graad = Properties.Settings.Default.listGraadMPU[index];
Toolbox = Properties.Settings.Default.listToolboxMPU[index];
}
}
if (compare == "AAUX")
{
var index = Properties.Settings.Default.listFoutcodeAAUX.FindIndex(a => a == item.FoutCode);
if (index == -1)
graad = "";
else
{
Toolbox = Properties.Settings.Default.listToolboxAAUX[index];
graad = Properties.Settings.Default.listGraadAAUX[index];
}
}
if (compare == "CTRL")
{
var index = Properties.Settings.Default.listFoutcodeCTRL.FindIndex(a => a == item.FoutCode);
if (index == -1)
graad = "";
else
{
Toolbox = Properties.Settings.Default.listToolboxACTRL[index];
graad = Properties.Settings.Default.listGraadCTRL[index];
}
}
string cat = Convert.ToString(item.Category);
if (cat == null)
cat = "Onbeschikbaar";
try
{
dataTreinFouten.Add(new FoutenMetNaam { Datum = item.Datum, FoutCode = item.FoutCode, Module = modNaam[0].ToUpper(), File = modNaam[1].ToUpper(), Name = item.Name, Omschrijving = item.Omschrijving, Time = item.Time, Graad = graad, FoutId = item.FoutId, Toolbox = Toolbox, Category = cat.ToUpper()});
}
catch (Exception ex)
{
Xceed.Wpf.Toolkit.MessageBox.Show(ex.Message);
}
}
}
The list is used to populate a datagrid, the class used for the list "dataTreinFouten":
class FoutenMetNaam
{
public DateTime Datum { get; set; }
public TimeSpan Time { get; set; }
public String FoutCode { get; set; }
public String Omschrijving { get; set; }
public String Module { get; set; }
public String Name { get; set; }
public int FoutId { get; set; }
public string Graad { get; set; }
public string File { get; set; }
public List<ExtraInfo> listInfoRondFout { get; set; }
public bool isManueel { get; set; }
public string Toolbox { get; set; }
public string Category { get; set; }
}
But I can't really get the state of how far the query is already. Which means I can't update a progress bar with it current state and because the database can contain over 30.000.000 results it is quite needed.
EDIT:
I added a Entity Data Model generated from my SQL database "Events".
Code to use this:
private EventsEntities eventsEntities = new EventsEntities();
Any help or pointing in the right direction is greatly appreciated!

Entity Framework is deleting an Entry

I'm fetching information from a webpage in two pages:
First page:
- Content c1 is created and a Translation c1.t1 is created;
- Content c2 is created and Translation c2.t1 is created;
Second page:
- The system detects that c1 already exists and just adds c1.t2 to the proper table;
- The system detects that c2 already exists and just adds c2.t2 to the proper table;
Somehow, on the second page, the system is overritting c1.t1 with c1.t2 and only the second translation is available on the database. When debbugging, found that it is deletting c1.t1 at some point but I couldn't figure out why.
This is my actual stuff:
EF 4.1
Code-First Aproach
DbContext
I have this POCO Entities (minimized):
RegionalContent: - It's like a tranlation and regional info about a content:
public class XBLRegionalContent
{
[Key, Column(Order = 0)]
public string ContentId { get; set; }
[ForeignKey("ContentId")]
public virtual XBLContent Content { get; set; }
[Key, Column(Order = 1)]
public string RegionId { get; set; }
[ForeignKey("RegionId")]
public virtual XBLRegion Region { get; set; }
public string Name { get; set; }
}
Content: - Unique content per GUID:
public class XBLContent
{
#region [ Properties ]
/// <summary>
/// The GUID
/// </summary>
[Key]
[StringLength(36, ErrorMessage="Must have 36 characters")]
[Required(ErrorMessage="Must have a unique GUID")]
public string GUID { get; set; }
public string Type { get; set; }
public virtual ICollection<XBLRegionalContent> RegionalInfo { get; set; }
}
Region - Pretty straight forward:
public class XBLRegion
{
[Key]
[StringLength(5, ErrorMessage="ID must have 5 characters")]
[Required]
[RegularExpression(#"[a-z]{2}-[A-Z]{2}", ErrorMessage = "ID must be in ISO 639 standard")]
public string ID { get; set; }
public string Country { get; set; }
public string Language { get; set; }
}
DbContext class has nothing different, just DbSets.
One content has many translations. One translation has one content related. The translation primary key is compound of content guid and region id.
I have a class in Model that populates the database and creates a local list that the View uses to display information. That way, I only access the Database one time to save, and don't need to retrieve information when it is saved.
Here is only the important information about this class:
public class XBLChart : IDisposable
{
XBLContentContext db = new XBLContentContext();
private string baseurl = "http://foo.bar/";
public string Locale { get; private set; }
public string HTML { get; private set; }
public string URL { get; set; }
public ContentType Type { get; private set; }
public List<XBLContent> Contents { get; set; }
public XBLChart(ContentType type, string sort, string locale)
{
Type = type;
if (sort == null)
sort = Enum.GetName(typeof(SortBy), SortBy.OfferStartDate);
if (locale != null && locale.Length == 5)
Locale = locale;
else
Locale = "en-US";
URL = baseurl + Locale + "/" + sort;
HTML = FeedUtils.RequestHTML(URL);
Contents = new List<XBLContent>();
PopulateList();
}
private void PopulateList()
{
MatchCollection itens = Regexes.ChartItems().Matches(HTML);
MatchCollection titulos = Regexes.ChartTitles().Matches(HTML);
int type = (int)Type;
int start = type * 12;
this.Title = HttpUtility.HtmlDecode(titulos[type].Groups["title"].Value);
if (titulos.Count < 8 && start > 1)
{
start = (type - 1) * 12;
type--;
}
XBLRegion region;
if (!db.XBLRegions.Any(x => x.ID == Locale))
{
region = new XBLRegion { ID = Locale };
db.XBLRegions.Add(region);
db.SaveChanges();
}
else
region = db.XBLRegions.SingleOrDefault(x => x.ID == Locale);
for (int i = start; i < (start + 2); i++)
{
string guid = itens[i].Groups["guid"].Value;
XBLContent c = new XBLContent(guid);
if (!db.XBLContents.Any(x => x.GUID == guid))
{
c.Type = Type.ToString();
c.PopularInfo(Locale);
db.XBLContents.Add(c);
}
else
c = db.XBLContents.Single(x => x.GUID == c.GUID);
XBLRegionalContent regionalcontent = new XBLRegionalContent(guid, Locale);
if (!db.XBLRegionalInfos.Any(x => x.ContentId == guid && x.RegionId == Locale))
{
if (c.HTML == null)
c.PopularInfo(Locale);
regionalcontent.Populate(c.HTML);
regionalcontent.Name = HttpUtility.HtmlDecode(itens[i].Groups["name"].Value);
db.XBLRegionalInfos.Add(regionalcontent);
}
else
regionalcontent = db.XBLRegionalInfos.Single(x => x.ContentId == guid && x.RegionId == Locale);
db.SaveChanges();
c.RegionalInfo.Clear();
regionalcontent.Region = region;
c.RegionalInfo.Add(regionalcontent);
Contents.Add(c);
}
}
}
you are missing a db.SaveChanges() after
db.SaveChanges();
c.RegionalInfo.Clear();
regionalcontent.Region = region;
c.RegionalInfo.Add(regionalcontent);

Categories