I looked all over the internet and still couldn't find a solution to this.
I tried the attach method:
public static void updatePhoto(string name, string albumName, string newName, string newPath)
{
//updates photo... no delete and adding...
var photo = new Image(){Label=newName, Path = newPath};
using (var db = new EzPrintsEntities())
{
db.Images.Attach(photo);
db.SaveChanges();
}
}
but that did not do anything at all.
So the question then is how do you implement an UPDATE to the sql database through EF in the code below?
public static void updatePhoto(string name, string albumName, string newName, string newPath)
{
EzPrintsEntities db = new EzPrintsEntities();
}
If you're updating an existing photo, you need to load it, and change the existing value:
public static void updatePhoto(string name, string albumName, string newName, string newPath)
{
using (var db = new EzPrintsEntities())
{
// Load photo
var photo = db.Images.FirstOrDefault(i => i.Label == name && i.Album == albumName);
if (photo == null)
{
// no matching photo - do something
}
// Update data
photo.Label = newName;
photo.Path = newPath;
db.SaveChanges();
}
}
The simplist way would be:
public static void updatePhoto(string name, string albumName, string newName, string newPath)
{
//updates photo... no delete and adding...
using (var db = new EzPrintsEntities())
{
var photo = (from p in db.Images
where p.name == name &&
p.albumname == albumName
select p).First();
photo.name = newName;
photo.path = newPath;
db.SaveChanges();
}
}
You simply select the existing photo object using Linq, modify it, and SaveChanges()
What you want to do is also pass to your updatePhoto method the value(s) for the primary key on your Image entity. Then instead of creating a new Image entity and attaching it and saving the context, you'll get the Image entity from your context, and just update the properties on it.
Something along these lines:
using (var db = new EzPrintsEntities())
{
var image = db.Images.SingleOrDefault(i => i.Id == id); // Assuming Id is the PK on Image, and we sent in the PK in a variable called id.
if (image != null)
{
image.Label = newName;
image.Path = newPath;
db.SaveChanges();
}
else
{
// Invalid PK value sent in, do something here (logging, error display, whatever).
}
}
Related
I have a WPF app running against DB2 9.1 (z/OS) using EF5. Everything works fine except for inserting where the primary key is an INTEGER(10) field.
The entity has the mapped property as an int type.
private Models.UserInfo GetUserInfo(String emplID, String firstName, String lastName)
{
if (String.IsNullOrEmpty(emplID))
return null;
int _emplID = Convert.ToInt32(emplID.Trim());
try
{
using (var ctx = new Data.TIMSContext())
{
var user = (from u in ctx.Query<Data.Entities.ASNUser>()
where u.EmployeeID == _emplID
select u).FirstOrDefault();
if (user == null)
{
//add user to database
user = new Data.Entities.ASNUser()
{
EmployeeID = _emplID,
FirstName = firstName.Trim(),
LastName = lastName.Trim()
};
ctx.Set<Data.Entities.ASNUser>().Add(user);
ctx.SaveChanges();
}
return new Models.UserInfo()
{
EmployeeID = user.EmployeeID,
DisplayName = String.Format("{0}, {1}", user.LastName, user.FirstName)
};
}
}
catch (Exception e)
{
throw;
}
}
This could be a problem with your edmx file / EF model.
If the EmployeeID field is defined in the model as a auto increment id, then EF will ignore the value that you set and null will be sent to the database. But the database does not have an auto increment field, so it tries to insert null.
I am trying to use one attach to the EF to update all the records I need.
public void UpdateSale(Sale s)
{
Context.Sales.Attach(s);
Context.Entry(s).State = System.Data.Entity.EntityState.Modified;
Context.SaveChanges();
}
Lets Say like so. The above code gives me an error because it says the primary keys I am trying to add already exist(They arent automatically generated yet)
Now Sale has a number of different other Entity Models inside it like:
SavedForm, ProductSale
Now the code calling the UpdateSale is here
public JsonResult AddNewForms(string Anamaka, string NispahB, string Hazaot, string ManufactorerID, string ClientStatus, string TypeFile)
{
BL.FormConnectorLogic fcl = new BL.FormConnectorLogic();
DAL.SavedForm AnamakaForm = MakeSavedForm(Boolean.Parse(Anamaka),"מסמך הנמקה",ClientStatus);
DAL.SavedForm NispahBForm = MakeSavedForm(Boolean.Parse(NispahB), "נספח ב", ClientStatus);
DAL.SavedForm HazaotForm = MakeSavedForm(Boolean.Parse(Hazaot), TypeFile, ClientStatus, ManufactorerID);
var results = new { A = AnamakaForm, N = NispahBForm, H = HazaotForm };
return Json(results, JsonRequestBehavior.AllowGet);
}
public DAL.SavedForm MakeSavedForm(bool Authorized, string FormName, string ClientStatus, string ManufactorerID = "")
{
DAL.Sale s = (DAL.Sale)Session["SaleSave"];
DAL.SavedForm sf = new DAL.SavedForm();
if (Authorized)
{
sf = new DAL.SavedForm();
sf.FormName = new BL.FormConnectorLogic().getFormByName(FormName, ClientStatus, ManufactorerID).FormName;
sf.DateFormed = DateTime.Now;
sf.AgentID = s.AgentID;
sf.Status = "פתוח";
sf.SaleID = s.ID;
s.SavedForms.Add(sf);
new BL.SaleLogic().UpdateSale(s);
Session["SaleSave"] = s;
return sf;
}
else return null;
}
Now I've read up on the State and there is a difference between Added and Modified.
While I can't really seem to tell when I am going to add and when I am going to modify.
Is there anyway to disregard everything and to just shove my whole class and all its relationships to the database?
I have the following code which takes a CSV and writes to a console:
using (CsvReader csv = new CsvReader(
new StreamReader("data.csv"), true))
{
// missing fields will not throw an exception,
// but will instead be treated as if there was a null value
csv.MissingFieldAction = MissingFieldAction.ReplaceByNull;
// to replace by "" instead, then use the following action:
//csv.MissingFieldAction = MissingFieldAction.ReplaceByEmpty;
int fieldCount = csv.FieldCount;
string[] headers = csv.GetFieldHeaders();
while (csv.ReadNextRecord())
{
for (int i = 0; i < fieldCount; i++)
Console.Write(string.Format("{0} = {1};",
headers[i],
csv[i] == null ? "MISSING" : csv[i]));
Console.WriteLine();
}
}
The CSV file has 7 headers for which I have 7 columns in my SQL table.
What is the best way to take each csv[i] and write to a row for each column and then move to the next row?
I tried to add the ccsv[i] to a string array but that didn't work.
I also tried the following:
SqlCommand sql = new SqlCommand("INSERT INTO table1 [" + csv[i] + "]", mysqlconnectionstring);
sql.ExecuteNonQuery();
My table (table1) is like this:
name address city zipcode phone fax device
your problem is simple but I will take it one step further and let you know a better way to approach the issue.
when you have a problem to sold, always break it down into parts and apply each part in each own method. For example, in your case:
1 - read from the file
2 - create a sql query
3 - run the query
and you can even add validation to the file (imagine your file does not even have 7 fields in one or more lines...) and the example below it to be taken, only if your file never passes around 500 lines, as if it does normally you should consider to use a SQL statement that takes your file directly in to the database, it's called bulk insert
1 - read from file:
I would use a List<string> to hold the line entries and I always use StreamReader to read from text files.
using (StreamReader sr = File.OpenText(this.CsvPath))
{
while ((line = sr.ReadLine()) != null)
{
splittedLine = line.Split(new string[] { this.Separator }, StringSplitOptions.None);
if (iLine == 0 && this.HasHeader)
// header line
this.Header = splittedLine;
else
this.Lines.Add(splittedLine);
iLine++;
}
}
2 - generate the sql
foreach (var line in this.Lines)
{
string entries = string.Concat("'", string.Join("','", line))
.TrimEnd('\'').TrimEnd(','); // remove last ",'"
this.Query.Add(string.Format(this.LineTemplate, entries));
}
3 - run the query
SqlCommand sql = new SqlCommand(string.Join("", query), mysqlconnectionstring);
sql.ExecuteNonQuery();
having some fun I end up doing the solution and you can download it here, the output is:
The code can be found here. It needs more tweaks but I will left that for others. Solution written in C#, VS 2013.
The ExtractCsvIntoSql class is as follows:
public class ExtractCsvIntoSql
{
private string CsvPath, Separator;
private bool HasHeader;
private List<string[]> Lines;
private List<string> Query;
/// <summary>
/// Header content of the CSV File
/// </summary>
public string[] Header { get; private set; }
/// <summary>
/// Template to be used in each INSERT Query statement
/// </summary>
public string LineTemplate { get; set; }
public ExtractCsvIntoSql(string csvPath, string separator, bool hasHeader = false)
{
this.CsvPath = csvPath;
this.Separator = separator;
this.HasHeader = hasHeader;
this.Lines = new List<string[]>();
// you can also set this
this.LineTemplate = "INSERT INTO [table1] SELECT ({0});";
}
/// <summary>
/// Generates the SQL Query
/// </summary>
/// <returns></returns>
public List<string> Generate()
{
if(this.CsvPath == null)
throw new ArgumentException("CSV Path can't be empty");
// extract csv into object
Extract();
// generate sql query
GenerateQuery();
return this.Query;
}
private void Extract()
{
string line;
string[] splittedLine;
int iLine = 0;
try
{
using (StreamReader sr = File.OpenText(this.CsvPath))
{
while ((line = sr.ReadLine()) != null)
{
splittedLine = line.Split(new string[] { this.Separator }, StringSplitOptions.None);
if (iLine == 0 && this.HasHeader)
// header line
this.Header = splittedLine;
else
this.Lines.Add(splittedLine);
iLine++;
}
}
}
catch (Exception ex)
{
if(ex.InnerException != null)
while (ex.InnerException != null)
ex = ex.InnerException;
throw ex;
}
// Lines will have all rows and each row, the column entry
}
private void GenerateQuery()
{
foreach (var line in this.Lines)
{
string entries = string.Concat("'", string.Join("','", line))
.TrimEnd('\'').TrimEnd(','); // remove last ",'"
this.Query.Add(string.Format(this.LineTemplate, entries));
}
}
}
and you can run it as:
class Program
{
static void Main(string[] args)
{
string file = Ask("What is the CSV file path? (full path)");
string separator = Ask("What is the current separator? (; or ,)");
var extract = new ExtractCsvIntoSql(file, separator);
var sql = extract.Generate();
Output(sql);
}
private static void Output(IEnumerable<string> sql)
{
foreach(var query in sql)
Console.WriteLine(query);
Console.WriteLine("*******************************************");
Console.Write("END ");
Console.ReadLine();
}
private static string Ask(string question)
{
Console.WriteLine("*******************************************");
Console.WriteLine(question);
Console.Write("= ");
return Console.ReadLine();
}
}
Usually i like to be a bit more generic so i'll try to explain a very basic flow i use from time to time:
I don't like the hard coded attitude so even if your code will work it will be dedicated specifically to one type. I prefer i simple reflection, first to understand what DTO is it and then to understand what repository should i use to manipulate it:
For example:
public class ImportProvider
{
private readonly string _path;
private readonly ObjectResolver _objectResolver;
public ImportProvider(string path)
{
_path = path;
_objectResolver = new ObjectResolver();
}
public void Import()
{
var filePaths = Directory.GetFiles(_path, "*.csv");
foreach (var filePath in filePaths)
{
var fileName = Path.GetFileName(filePath);
var className = fileName.Remove(fileName.Length-4);
using (var reader = new CsvFileReader(filePath))
{
var row = new CsvRow();
var repository = (DaoBase)_objectResolver.Resolve("DAL.Repository", className + "Dao");
while (reader.ReadRow(row))
{
var dtoInstance = (DtoBase)_objectResolver.Resolve("DAL.DTO", className + "Dto");
dtoInstance.FillInstance(row.ToArray());
repository.Save(dtoInstance);
}
}
}
}
}
Above is a very basic class responsible importing the data. Nevertheless of how this piece of code parsing CSV files (CsvFileReader), the important part is thata "CsvRow" is a simple List.
Below is the implementation of the ObjectResolver:
public class ObjectResolver
{
private readonly Assembly _myDal;
public ObjectResolver()
{
_myDal = Assembly.Load("DAL");
}
public object Resolve(string nameSpace, string name)
{
var myLoadClass = _myDal.GetType(nameSpace + "." + name);
return Activator.CreateInstance(myLoadClass);
}
}
The idea is to simple follow a naming convetion, in my case is using a "Dto" suffix for reflecting the instances, and "Dao" suffix for reflecting the responsible dao. The full name of the Dto or the Dao can be taken from the csv name or from the header (as you wish)
Next step is filling the Dto, each dto or implements the following simple abstract:
public abstract class DtoBase
{
public abstract void FillInstance(params string[] parameters);
}
Since each Dto "knows" his structure (just like you knew to create an appropriate table in the database), it can easily implement the FillInstanceMethod, here is a simple Dto example:
public class ProductDto : DtoBase
{
public int ProductId { get; set; }
public double Weight { get; set; }
public int FamilyId { get; set; }
public override void FillInstance(params string[] parameters)
{
ProductId = int.Parse(parameters[0]);
Weight = double.Parse(parameters[1]);
FamilyId = int.Parse(parameters[2]);
}
}
After you have your Dto filled with data you should find the appropriate Dao to handle it
which is basically happens in reflection in this line of the Import() method:
var repository = (DaoBase)_objectResolver.Resolve("DAL.Repository", className + "Dao");
In my case the Dao implements an abstract base class - but it's not that relevant to your problem, your DaoBase can be a simple abstract with a single Save() method.
This way you have a dedicated Dao to CRUD your Dto's - each Dao simply knows how to save for its relevant Dto. Below is the corresponding ProductDao to the ProductDto:
public class ProductDao : DaoBase
{
private const string InsertProductQuery = #"SET foreign_key_checks = 0;
Insert into product (productID, weight, familyID)
VALUES (#productId, #weight, #familyId);
SET foreign_key_checks = 1;";
public override void Save(DtoBase dto)
{
var productToSave = dto as ProductDto;
var saveproductCommand = GetDbCommand(InsertProductQuery);
if (productToSave != null)
{
saveproductCommand.Parameters.Add(CreateParameter("#productId", productToSave.ProductId));
saveproductCommand.Parameters.Add(CreateParameter("#weight", productToSave.Weight));
saveproductCommand.Parameters.Add(CreateParameter("#familyId", productToSave.FamilyId));
ExecuteNonQuery(ref saveproductCommand);
}
}
}
Please ignore the CreateParameter() method, since it's an abstraction from the base classs. you can just use a CreateSqlParameter or CreateDataParameter etc.
Just notice, it's a real naive implementation - you can easily remodel it better, depends on your needs.
From the first impression of your questionc I guess you would be having hugely number of records (more than lacs). If yes I would consider the SQL bulk copies an option. If the record would be less go ahead single record. Insert. The reason for you insert not working is u not providing all the columns of the table and also there's some syntax error.
I am using this code to create a record in the ClientAccountAccess table. However there should only ever be one record in this table at any time. so if this function is called I first want to check if a record exists, if it does delete it then add the new record.
private static void SetAccessCode(string guidCode)
{
using (EPOSEntities db = new EPOSEntities())
{
//so here would I say something like (see below)
ClientAccountAccess client = new ClientAccountAccess();
client.GUID = guidCode;
db.AddToClientAccountAccesses(client);
db.SaveChanges();
}
}
//
ClientAccountAccess clientAccessCodes = db.ClientAccountAccesses
.OrderByDescending(x => x.Id)
.Take(1)
.Single();
if clientAccessCodes.exists()
db.DeleteObject(clientAccessCodes);
db.SaveChanges();
Try something like this...
bool doesItExistAlready = (from caa in db.ClientAccountAccesses
where css.id == guidCode
select caa).Any();
if (doesItExistAlready)
{
// Delete old record
db.DeleteObject(PUTIDENTIFIERHERE);
}
// Add new record
ClientAccountAccess client = new ClientAccountAccess();
client.GUID = guidCode;
db.AddToClientAccountAccesses(client);
Do you really need to remove it? If you don't you can do this:
private static void SetAccessCode(string guidCode)
{
using (EPOSEntities db = new EPOSEntities())
{
var c= db.ClientAccountAccesses.FirstOrDefault(f=>f.GUID==guidCode);
if(c!=null)
return;
var client = new ClientAccountAccess(){GUID=guidCode};
db.AddToClientAccountAccesses(client);
db.SaveChanges();
}
}
Then you will only insert it if it do not already exists
If you need to remove it before creating the object. You can do this:
private static void SetAccessCode(string guidCode)
{
using (EPOSEntities db = new EPOSEntities())
{
var c= db.ClientAccountAccesses.FirstOrDefault(f=>f.GUID==guidCode);
if(c!=null)
{
db.ClientAccountAccesses.DeleteObject(c);
db.SaveChanges();
}
var client = new ClientAccountAccess(){GUID=guidCode};
db.AddToClientAccountAccesses(client);
db.SaveChanges();
}
}
If you can update the object you can do something like this:
private static void SetAccessCode(string guidCode)
{
using (EPOSEntities db = new EPOSEntities())
{
var c= db.ClientAccountAccesses.FirstOrDefault(f=>f.GUID==guidCode);
if(c==null)
{
c=new ClientAccountAccess();
db.AddToClientAccountAccesses(client);
}
c.GUID=guidCode;
db.SaveChanges();
}
}
I want to upsert reference members of an existing entity.
Do I have to write specific code for the upsert?
meaning: I have to check if I'm handling an existing reference member or a new one.
Is there any other simple way to do so?
What happens when you do only Save ?
public void SaveCofiguration(MamConfiguration_V1Ui itemUi)
{
var itemEf = mMamConfiguration_V1UiToEfConvertor.ConvertToNewEf(itemUi);
using (var maMDBEntities = new MaMDBEntities())
{
IDal<MamConfiguration_V1> mamConfigurationDal = mDalFactory.GetDal<MamConfiguration_V1>(maMDBEntities);
mamConfigurationDal.Save(itemEf);
}
}
public MamConfiguration_V1 GetById(object id)
{
id.ThrowIfNull("id");
int configurationId = Convert.ToInt32(id);
var result =
mMaMDBEntities.MamConfiguration_V1.SingleOrDefault(item => item.ConfigurationId == configurationId);
return result;
}
public MamConfiguration_V1 Save(MamConfiguration_V1 item)
{
item.ThrowIfNull("item");
var itemFromDB = GetById(item.ConfigurationId);
if (itemFromDB != null)
{
UpdateEfItem(itemFromDB, item);
// if (mMaMDBEntities.ObjectStateManager.GetObjectStateEntry(itemFromDB).State == EntityState.Detached)
// {
// mMaMDBEntities.MamConfiguration_V1.AddObject(itemFromDB);
// }
// Attached object tracks modifications automatically
mMaMDBEntities.SaveChanges();
return item;
}
private void UpdateEfItem(MamConfiguration_V1 itemFromDb, MamConfiguration_V1 itemFromUi)
{
itemFromDb.UpdatedDate = DateTime.Now;
itemFromDb.Description = itemFromUi.Description;
itemFromDb.StatusId = itemFromUi.StatusId;
itemFromDb.Name = itemFromUi.Name;
itemFromDb.NumericTraffic = itemFromUi.NumericTraffic;
itemFromDb.PercentageTraffic = itemFromUi.PercentageTraffic;
itemFromDb.Type = itemFromUi.NumericTraffic;
foreach (var item in itemFromDb.MamConfigurationToBrowser_V1.ToList())
{
if (itemFromUi.MamConfigurationToBrowser_V1.All(b => b.BrowserVersionId != item.BrowserVersionId))
{
mMaMDBEntities.MamConfigurationToBrowser_V1.DeleteObject(item);
}
}
for (int i = 0; i < itemFromUi.MamConfigurationToBrowser_V1.Count; i++)
{
var element = itemFromUi.MamConfigurationToBrowser_V1.ElementAt(i);
var item = itemFromDb.MamConfigurationToBrowser_V1.SingleOrDefault(b => b.BrowserVersionId == element.BrowserVersionId);
if (item != null)
{
// copy properties from element to item
}
else
{
element.Browser = mMaMDBEntities.Browsers.Single(browserItem =>
browserItem.BrowserID == element.BrowserID);
//element.MamConfiguration_V1 = itemFromDb;
//have also tried: element.MamConfiguration_V1 = null;
//element.MamConfiguration_V1Reference = null;
itemFromDb.MamConfigurationToBrowser_V1.Add(element);
}
}
}
But I would have expecte Save(itemUi) and SaveChanges() to work fine. No?
public void InsertOrUpdate(DbContext context, UEntity entity)
{
context.Entry(entity).State = entity.Id == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
http://forums.asp.net/t/1889944.aspx/1
To avoid the overhead of a query and then insert, or throwing exceptions, you can take advantage of the underlying database support for merges or upserts.
This nuget package does the job pretty well: https://www.nuget.org/packages/FlexLabs.EntityFrameworkCore.Upsert/
Github: https://github.com/artiomchi/FlexLabs.Upsert
Example:
DataContext.DailyVisits
.Upsert(new DailyVisit
{
// new entity path
UserID = userID,
Date = DateTime.UtcNow.Date,
Visits = 1,
})
// duplicate checking fields
.On(v => new { v.UserID, v.Date })
.WhenMatched((old, #new) => new DailyVisit
{
// merge / upsert path
Visits = old.Visits + 1,
})
.RunAsync();
The underlying generated sql does a proper upsert. This command runs right away and does not use change tracking, so that is one limitation.
See 'AddOrUpdate' method of System.Data.Entity.Migrations.
http://msdn.microsoft.com/en-us/library/system.data.entity.migrations.idbsetextensions.addorupdate%28v=vs.103%29.aspx
using System.Data.Entity.Migrations;
public void Save(Person person) {
var db = new MyDbContext();
db.People.AddOrUpdate(person);
db.SaveChanges();
}
"optimistic" approach for simple scenarios (demos)...
dbContext.Find()'s intellisense help tells us that it either retrieves entity by key if already present in current context, or queries the database to get it... then we know if it exists to either add or update. i'm using EFCore v2.2.0.
var existing = _context.Find<InventoryItem>(new object[] {item.ProductId});
if (existing == null) _context.Add(item);
else existing.Quantity = item.Quantity;
_context.SaveChanges();
DbContext.Update Method
For entity types with generated keys if an entity has its primary key value set then it will be tracked in the Modified state. If the primary key value is not set then it will be tracked in the Added state. This helps ensure new entities will be inserted, while existing entities will be updated. An entity is considered to have its primary key value set if the primary key property is set to anything other than the CLR default for the property type.
For entity types without generated keys, the state set is always Modified.
read this article
you can use this sample