LINQ to SQL does not work properly with Automapper - c#

I have used Automapper for a long time without any difficulty for a long time and I have been trying to integrate this wonderful tool into my LINQ to SQl framework recently. I have following code:
using (var ps = new Promotionalsponsorship(constr))
{
var applicationToSave = Mapper.Map<ApplicationModel, Application>(application);
if (applicationToSave.ApplicationId == default(int))
{
ps.Application.InsertOnSubmit(applicationToSave);
}
ps.Application.Context.SubmitChanges();
}
What I am trying to do here is UPSERT method where I am saying that if there is anew record with id = 0 then insert OR update. Funny thing here is that insert works BUT ps.Application.Context.SubmitChanges(); does not work when I update the context using Automapper as I did that in the code above.
Now, if I update context using simple property assignment like following:
applicationToSave.Name = "Beautiful";
if (applicationToSave.ApplicationId == default(int))
{
ps.Application.InsertOnSubmit(applicationToSave);
}
ps.Application.Context.SubmitChanges();
This one works. I feel like Automapper does something to context object which makes it detached from the DB. I am not sure. How can I make it work with AutoMapper?

Change tracking only works for loaded entities. Automapper is creating a new object for you, which is completely unrelated to EF - it's just a normal object as far as the code cares. You'll need to do something like this:
using (var ps = new Promotionalsponsorship(constr))
{
var applicationToSave = ps.Application.Find(application.ApplicationId);
if (applicationToSave == null)
applicationToSave = new Application();
Mapper.Map(application, applicationToSave); //May not be accurate, search for the
//method that writes into an existing object
if (applicationToSave.ApplicationId == default(int))
{
ps.Application.InsertOnSubmit(applicationToSave);
}
ps.Application.Context.SubmitChanges();
}
That is, you load the existing application (or create a new one), then fill it with automapper.

Related

Returning Id's from a list in Entity Framework

I'm having an issue with Entity Framework, when I execute SaveChanges, the context has quite a few objects associated with it, some of the objects are updated and some are added, afterwards I want to use the Id's of all these items (the Id's for the added items are only assigned on insert in the database). After the save changes a list of all the objects is empty.
I've seen samples on the site where the object is updated after the save so I suspect it might be how I'm getting the list of objects in the first place
Here's my code:
// Lots of processing to create or update objects
using (var localContext = this.context)
{
var updatedObjects = localContext.ChangeTracker.Entries().Where(e => e.Entity is GenerationEvent && (e.State == EntityState.Modified || e.State == EntityState.Added));
var updatedEvents = updatedObjects.Select(e => (GenerationEvent)e.Entity);
// The list has 5 items in at this point
localContext.SaveChanges();
// This list is now empty
DoSomethingWithList(updatedEvents);
}
Thanks in advance for any help.
The variable updatedEvents is a Linq query. Linq queries aren't executed immediately. By the time it is executed in your code it won't find any updated object anymore. Putting .ToList() after the Linq query will execute it immediately.
var updatedEvents = updatedObjects.Select(e => (GenerationEvent)e.Entity).ToList();
first your "using" statement is odd.
it should be
using (var context = new MyContext()) //passing in optional connection string.
{
}
Then the way you access your entities seem odd or i have no clue what you are doing there...
var updatedObjects = localContext.ChangeTracker.Entries().Where(e => e.Entity is GenerationEvent && (e.State == EntityState.Modified || e.State == EntityState.Added));
var updatedEvents = updatedObjects.Select(e => (GenerationEvent)e.Entity);
Seems like you are asking the context for all items which are considered "Add" or "Updated"
Then you are accepting the changes to the context. eg SaveChanges().
I fully expect "updatedEvents" to be empty, after save-changes is called.
Change you stuff... to something like
using (var context = new MyContext()) //passing in optional connection string.
{
LIst<EntityType> listOfChangedEntities = //ToDo:either passed in or generated
context.EntityType.AddRandge(listOfChangedEntities);
context.SaveChanges();
//after SaveChanges has been done all the entities in the
//listOfChangedEntities will now have there id's
//for update, fetch the entities... change them and Update them
}
I suspect that you are trying to create some sort of generic code to handle any type of Entity without specifying its type. Your code is not suited for this as it is, if this is what you are trying to do, I would modify the question to ask what you are trying to achieve. But the above is the Normal way of getting the Id's of the entities which have been inserted.
The other examples you are passably talking about is where they use foreign keys and navigation properties to automatically associate related entities, but your code looks way off from that.
UPDATE
routine
public static DoWork()
{
var context = new MyContext();
List<GenerationEvent > internalEntityType = new List<GenerationEvent ();
foreach(var item in SomeList)
{
var newItemEntity = new GenerationEvent();
newItemEntity.Name = "Test";
context.GenerationEvent.Add(newItemEntity);
//add to internal list
internalEntityType.Add(newItemEntity )
}
context.SaveChanges();
var first_id = internalEntityType.FirstOrDefault().Id;
//first_id will not be 0 it will be the Id the database gave it.
}

Cannot add an entity with a key that is already in use update operation

I am creating small application in which i have used LINQ To SQL to perform all operation to database.
Now here i am giving the small part of my database structure please take a look.
So update language detail i am getting the object of login using the datacontext something like this.
XVDataContext Context = new XVDataContext ();
var myQuery = from objLogIn in Context.GetTable<LogIn>() where objLogIn.Emp_Id == nEmpId select objLogIn;
In nEmpId i will always have some value.
So it is not creating any problem in fact i am getting the required record from DB and storing it in objUser object using the following code.
LogIn objUser = myQuery.First<LogIn>();
Now to update LanguageDetail i am executing following code but it throws Exception when i execute SubmitChanges line.
Here is the code that i am executing to update.
LanguageDetail obj = new LanguageDetail();
foreach (string sLanguages in TextBoxLanguagesKnown.Text.Split('\n'))
{
obj.Emp_Id = objUser.Emp_Id;
obj.Language = sLanguages.Trim();
}
objUser.LanguageDetails[0] = obj;
Context.SubmitChanges();
I already read following links.
cannot add an entity with a key that is already in use
LINQ To SQL exception with Attach(): Cannot add an entity with a key that is alredy in use
Cannot add an entity with a key that is already in use (LINQ)
By reading the above links i found that i am doing some mistake in ID fields but still i am unable to resolve.
Please tell me the clear understanding of raising this issue and how can i resolve this.
EDIT:
I simply want to update LanguageDetail table.
When i try to add new object using following code it still throws exception.
objUser.LanguageDetail.Add(obj);
You might want to add / remove languages for specific user by using following code.
var languages = TextBoxLanguagesKnown.Text.Split('\n');
// Removes deleted languages (first find all language details that are missing from the UI).
var deletedLanguages = objUser.LanguageDetails.Where(ld => !languages
.Any(l => ld.Language == l.Trim())).ToArray();
foreach(var deletedLanguage in deletedLanguages)
{
objUser.LanguageDetails.Remove(deletedLanguage);
Context.LanguageDetails.DeleteOnSubmit(deletedLanguage);
}
// Adds new languages (then adds new language details that are not found in the database).
var newLanguages = languages.Where(l => !objUser.LanguageDetails
.Any(ld => ld.Language == l.Trim())).ToArray();
foreach (string newLanguage in newLanguages)
{
var languageDetail = new LanguageDetail
{
Emp_Id = objUser.Emp_Id,
Language = newLanguage.Trim()
};
objUser.LanguageDetails.Add(languageDetail);
}
Context.SubmitChanges();
From my understanding you want to update the LanguageDetail entity in your database. In order to do so you have to do one of the following:
Retrieve the original LanguageDetail object based on its id, and update that object instead of creating a new one and assigning it the id of an existing object.
Attach the newly created object to your context instead of just giving a reference to it to your LanguageDetails collection.
The exception you are seeing happens because the way linq to sql behaves is that it threats the obj as a new object that you want to insert and because of that it tries to insert it into the language details table.
Modifying your code like that should work:
Context.LanguageDetails.Attach(obj);
objUser.Employee_LanguageDetails[0] = obj;

The property 'StatusId' is part of the object's key information and cannot be modified

I am using entity frame work when I tried to modify rm.StatusId = 3;
I get the title question error.
Also please give me links to know each and everything about entity frame work with asp .net controlls.
if (chk.Checked == true)
{
MAtt_RequestMaster rm =
ctx.MAtt_RequestMaster.SingleOrDefault(p => p.AutoID ==id);
rm.StatusId = 3;
ctx.SaveChanges();
}
im not too familliar with lambda expressions, but i think you can do this:
MAtt_RequestMaster rm =
ctx.MAtt_RequestMaster.SingleOrDefault(p => p.AutoID ==id);
rm.YourStatusTable = (from x in ctx.YourStatusTable
where x.statusID == 3
select x).FirstOrDefault();
ctx.SaveChanges();
This problem happens because you are referencing the same object more than once. This is not a limitation of EF, but rather a safety feature to ensure you are not inserting the same object with two different IDs. So to achieve what you are trying to do, is simply create a new object and add the newly created object to the database.
** This issue often happens inside loops. If you are using a while or foreach loop, make sure to have the New Created Object INSIDE the loop body.
try this:
MAtt_RequestMaster rm = new Matt_RequestMaster();
rm.StatusId = 3;
ctx.MAtt_RequestMasters.Attach(rm);
ctx.SaveChanges();

How can I correctly attach record object to data context in Entity Framework?

I need to switch data context for some records. So basically I have db context A and B, I fetch records using A, then I switch to B, alter records, and save them.
When I call Attach for B, I get exception that records are using by multiple data context, when I add Detach for A, I get exception, that records are not attached to A.
So how can I switch the data context?
Example
db_creator is creator of db context. Here I fetch the data (corrected version):
using (var db = db_creator.Create())
{
var coll = db.Mailing.Where(it => !it.mail_IsSent).ToList(); // (*)
coll.ForEach(it => db.Detach(it));
return coll;
}
(*) the mistake was caused by refactoring this piece, I created extra data context, and then later I tried to detach records from another one.
Now I would like to switch data context to new one, do some computation and modifications and save the records. coll is List of the records:
using (var db = db_creator.Create())
{
coll.ForEach(it => db.Mailing.Attach(it));
...
db.SaveChanges();
}
I recommend change your design and have ONE context at a time. (Based on your project type this could vary. Usually in web apps it's one context per http request.)
For example in a web application, you can do this like below:
protected MyContext Context
{
get
{
var context = HttpContext.Current.Items["MyContext"];
if (context == null)
{
context = new MyContext();
HttpContext.Current.Items.Add("MyContext", context);
}
return context as MyContext;
}
}
And dispose it in your Application_EndRequest:
app.EndRequest += (sender, args) =>
{
HttpContext.Current.Items.Remove("MyContext");
}
If you have multiple project types, then consider using an Ioc.
But if you still want to use two contexts, you can do as below(myEntity is your object you want to detach/attach):
if (context1.Entry(myEntity).State != EntityState.Detached);
{
((IObjectContextAdapter)context1).ObjectContext.Detach(myEntity);
}
context2.MyEntities.Attach(myEntity);
I have come to the conclusion that it's best (i.e. easier to avoid problems) to use ApplyCurrentValues instead of attaching. That is because when you call Attach there are several things going on that we don't know about, but which may surface in one way or the other through an exception. I prefer to do things the way I have control over what is done.
var myMailings = db_creator.Create().Mailing.Where(it => !it.mail_IsSent).ToList();
... // make modifications and retrieve coll a collection of Mailing objects
using (var db = db_creator.Create()) {
... // if you want to further modify the objects in coll you should do this before writing it to the context
foreach (Mailing it in coll) {
if (it.EntityKey != null) db.GetObjectByKey(it.EntityKey); // load entity
else db.Mailing.Single(x => x.YourId == it.YourId); // load the entity when EntityKey is not available, e.g. because it's a POCO
db.Mailing.ApplyCurrentValues(it); // sets the entity state to Modified
}
db.SaveChanges();
}
EDIT:
I tested the performance of this vs using Attach. It should be noted that for a simple table with an integer primary key, an int, a float and a string column for updating 1000 entries: the difference was 2.6s vs 0.27s, so this is significantly slower.
EDIT2:
A similar question was raised here. There the answer warned about using ApplyCurrentValues in conjunction with timestamp columns.
I also compared performance when loading the entity with db.GetObjectByKey(it.EntityKey) and there the performance difference is much smaller. ApplyCurrentValues then just takes 0.44s.

C# - Entity Framework - Understanding some basics

Model #1 - This model sits in a database on our Dev Server.
Model #1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png
Model #2 - This model sits in a database on our Prod Server and is updated each day by automatic feeds. alt text http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png
I have written what should be some simple code to sync my feed (Model #2) into my working DB (Model #1). Please note this is prototype code and the models may not be as pretty as they should. Also, the entry into Model #1 for the feed link data (mainly ClientID) is a manual process at this point which is why I am writing this simple sync method.
private void SyncFeeds()
{
var sourceList = from a in _dbFeed.Auto where a.Active == true select a;
foreach (RivWorks.Model.NegotiationAutos.Auto source in sourceList)
{
var targetList = from a in _dbRiv.Product where a.alternateProductID == source.AutoID select a;
if (targetList.Count() > 0)
{
// UPDATE...
try
{
var product = targetList.First();
product.alternateProductID = source.AutoID;
product.isFromFeed = true;
product.isDeleted = false;
product.SKU = source.StockNumber;
_dbRiv.SaveChanges();
}
catch (Exception ex)
{
string m = ex.Message;
}
}
else
{
// INSERT...
try
{
long clientID = source.Client.ClientID;
var companyDetail = (from a in _dbRiv.AutoNegotiationDetails where a.ClientID == clientID select a).First();
var company = companyDetail.Company;
switch (companyDetail.FeedSourceTable.ToUpper())
{
case "AUTO":
var product = new RivWorks.Model.Negotiation.Product();
product.alternateProductID = source.AutoID;
product.isFromFeed = true;
product.isDeleted = false;
product.SKU = source.StockNumber;
company.Product.Add(product);
break;
}
_dbRiv.SaveChanges();
}
catch (Exception ex)
{
string m = ex.Message;
}
}
}
}
Now for the questions:
In Model #2, the class structure for Auto is missing ClientID (see red circled area). Now, everything I have learned, EF creates a child class of Client and I should be able to find the ClientID in the child class. Yet, when I run my code, source.Client is a NULL object. Am I expecting something that EF does not do? Is there a way to populate the child class correctly?
Why does EF hide the child entity ID (ClientID in this case) in the parent table? Is there any way to expose it?
What else sticks out like the proverbial sore thumb?
TIA
1) The reason you are seeing a null for source.Client is because related objects are not loaded until you request them, or they are otherwise loaded into the object context. The following will load them explicitly:
if (!source.ClientReference.IsLoaded)
{
source.ClientReference.Load();
}
However, this is sub-optimal when you have a list of more than one record, as it sends one database query per Load() call. A better alternative is to the Include() method in your initial query, to instruct the ORM to load the related entities you are interested in, so:
var sourceList = from a in _dbFeed.Auto .Include("Client") where a.Active == true select a;
An alternative third method is to use something call relationship fix-up, where if, in your example for instance, the related clients had been queried previously, they would still be in your object context. For example:
var clients = (from a in _dbFeed.Client select a).ToList();
The EF will then 'fix-up' the relationships so source.Client would not be null. Obviously this is only something you would do if you required a list of all clients for synching, so is not relevant for your specific example.
Always remember that objects are never loaded into the EF unless you request them!
2) The first version of the EF deliberately does not map foreign key fields to observable fields or properties. This is a good rundown on the matter. In EF4.0, I understand foreign keys will be exposed due to popular demand.
3) One issue you may run into is the number of database queries requesting Products or AutoNegotiationContacts may generate. As an alternative, consider loading them in bulk or with a join on your initial query.
It's also seen as good practice to use an object context for one 'operation', then dispose of it, rather than persisting them across requests. There is very little overhead in initialising one, so one object context per SychFeeds() is more appropriate. ObjectContext implements IDisposable, so you can instantiate it in a using block and wrap the method's contents in that, to ensure everything is cleaned up correctly once your changes are submitted.

Categories

Resources