Insert Master-Detail with LINQ - c#

I want to insert a master-detail with the following structure:
Every Sale has an Id, date and a Client, Employee and SaleDetail.
Every Sales Detail has a pieces number and price and of course a reference to its master and which product is.
I tried the following code but I cannot get it to work:
private void GenerarNota()
{
EntityCollection<SalesDetail> details = new EntityCollection<SalesDetail>();
foreach (ListItem item in _productList)
{
SalesDetail detail = new SalesDetail();
detail.Product = db.Product.FirstOrDefault(p => p.Id == item.Id);
detail.Pieces = item.Pieces;
detail.Price = item.Price;
details.Add(detail);
}
Sale sale = new Sale
{
Client = (Client )txtCliente.Item,
Employee = (Employee )txtEmp.Item,
SalesDetail = details
};
db.AddToSale(sale);
db.SaveChanges();
}
The exception I got:
The object could not be added to the EntityCollection or
EntityReference. An object that is attached to an ObjectContext cannot
be added to an EntityCollection or EntityReference that is not
associated with a source object.
Am I doing something wrong? I read about attaching and dettaching objects but after I tried that I got a FK constraint violation.
Can you please tell me what I'm doing wrong or if it's another way to do it? I'm very new at LINQ, I could perfectly do it in pure SQL but I wanted to learn about it.

detail.Product is still associated with db. Prices and Pieces may be associated with it as well. The system is very strict in ensuring that every object can only be associated with one object context (your db) or completely detached (your EntityCollection<>).
Either your details EntityCollection needs to be associated with db (no idea how to do this) or all your objects must be properly detached before they're tacked into details.
Honestly, I'd just reorder things around - create Sale first and add it, then start inserting things into Sale.SalesDetail.

Related

Entity Framework The ObjectContext might be in an inconsistent state

I'd like to understand why "The ObjectContext might be in an inconsistent state." exception occurs.
I know that there are multiple threads related to similar issue but none seems to answer my question. (Closest to my problem is: The changes to the database were committed successfully...The ObjectContext might be in an inconsistent state )
I've got Database First model containing tables:
Training (PK Id)
User (PK Id)
UserTraining(PK (UserId, TrainingId), FK(UserId), FK(TrainingId))
Documentation(PK Id, FK TrainingId)
UserDocumentation (PK (UserId, TrainingId, DocumentationId),
FK(UserId, TrainingId), FK(DocumentationId))
All above tables are properly mapped in code(all keys are present etc.).
What I'm doing is - Create Training with Documentation, then Create UserTraining with UserDocumentation, like below:
var type = this.trainingRepository.GetTrainingType(command.Training.TypeId);
var training = new Training(
type,
command.Training.Name,
command.Training.Documentation,
...);
if (type.Equals(TrainingType.Mandatory))
{
var users = this.userRepository.GetAllUsers();
foreach(var user in users)
{
user.AssignTraining(training);//under the hood is created new instance of UserTraining with relation to training and UserDocumentation related to UserTraining and Documentation from training
}
}
trainingRepository.Add(training);
trainingRepository.SaveChanges();
There are 2 repositories but each uses the same instance of DBContext(registered as PerRequest).
Now if I move adding and saving changes above if (type.Equals(TrainingType.Mandatory)) and add one move SaveChanges at the end of method everything passes without any issues.
Unfortunately in case above it throws exception mentioned in title.
Now the question - Why does adding in the middle and then updating work and my solution doesn't? Can it be related to poor database schema or rather EF limitations?
AssignTraining method:
public void AssignTraining(Training training)
{
this.State.UserTrainings.Add(new UserTraining(training));
}
UserTraining constructor:
public UserTraining(Training training) : base(new
DataAccess.Database.UserTraining())
{
this.State.Training = training;
if(training.Documentation != null)
{
this.State.UserDocumentations.Add(new UserDocumentation(training.Documentation));
}
}

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;

Linq update query - Is there no pretty way?

I want to update my database using a LINQ2SQL query.
However this seems for some reason to be a very ugly task compared to the otherwise lovely LINQ code.
The query needs to update two tables.
tbl_subscription
(
id int,
sub_name nvarchar(100),
sub_desc nvarchar(500),
and so on.
)
tbl_subscription2tags
(
sub_id (FK to tbl_subscription)
tag_id (FK to a table called tbl_subscription_tags)
)
Now down to my update function a send a tbl_subscription entity with the tags and everything.
I can't find a pretty way to update my database..
I can only find ugly examples where I suddenly have to map all attributes..
There most be a smart way to perform this. Please help.
C# Example if possible.
I have tried this with no effect:
public void UpdateSubscription(tbl_subscription subscription)
{
db.tbl_subscriptions.Attach(subscription);
db.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, subscription);
db.SubmitChanges(System.Data.Linq.ConflictMode.FailOnFirstConflict);
}
Source for this code is here:
http://skyeyefive.spaces.live.com/blog/cns!6B6EB6E6694659F2!516.entry
Why don't just make the changes to the objects and perform a SubmitChanges to the DataContext?
using(MyDataContext dc = new MyDataContext("ConnectionString"))
{
foreach(var foo in dc.foo2)
{
foo.prop1 = 1;
}
dc.SubmitChanges();
}
Otherwise you need to tell us more about the lifecycle of the object you want to manipulate
edit: forgot to wrap in brackets for using
Unless I'm misunderstanding your situation, I think that citronas is right.
The best and easiest way that I've found to update database items through LINQ to SQL is the following:
Obtain the item you want to change from the data context
Change whatever values you want to update
Call the SubmitChanges() method of the data context.
Sample Code
The sample code below assumes that I have a data context named DBDataContext that connects to a database that has a Products table with ID and Price parameters. Also, a productID variable contains the ID of the record you want to update.
using (var db = new DBDataContext())
{
// Step 1 - get the item from the data context
var product = db.Products.Where(p => p.ID == productID).SingleOrDefault();
if (product == null) //Error checking
{
throw new ArgumentException();
}
// Step 2 - change whatever values you want to update
product.Price = 100;
// Step 3 - submit the changes
db.SubmitChanges();
}
I found out that you can use "Attach" as seen in my question to update a table, but apparently not the sub tables. So I just used a few Attach and it worked without having to run through parameters!

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.

Linq2SQL: Update object not created in datacontext

Normally when you update an object in linq2sql you get the object from a datacontext and use the same datacontext to save the object, right?
What's the best way to update a object that hasn't been retreived by that datacontext that you use to perform the save operation, i.e. I'm using flourinefx to pass data between flex and asp.net and when object return from the client to be saved I don't know how to save the object?
public static void Save(Client client)
{
CompanyDataContext db = new CompanyDataContext();
Validate(client);
if(client.Id.Equals(Guid.Empty))
{
//Create (right?):
client.Id = Guid.NewGuid();
db.Clients.InsertOnSubmit(client);
db.SubmitChanges();
}
else
{
//Update:
OffertaDataContext db = new OffertaDataContext();
db.Clients.????
}
}
Update: different approaches to use Attach doens't work in this case. So I guess a reflection based approach is required.
To update an existing but disconnected object, you need to "attach" it do the data context. This will re-use the existing primary key etc. You can control how to handle changes- i.e. treat as dirty, or treat as clean and track future changes, etc.
The Attach method is on the table - i.e.
ctx.Customers.Attach(customer); // optional bool to treat as modified
I think you have 2 options here:
1) Attach the object to the DataContext on which you will do your save
2) Using the primary key on your object, grab an instance that is attached to your context (e.g. do a FirstOrDefault()), and then copy the data over from the modified object to the object that has a context (reflection might be useful here).
Rick Strahl has a very good blog article on attaching entities to a context at http://www.west-wind.com/weblog/posts/134095.aspx, particularly in regards to some of the problems you might encounter.
I am hoping you can help. I am developing a tiered website using Linq to Sql. I created a new class(or object) in DBML designer called memberState. This object is not an actual table in the database. I have this method in my middle layer:
public override IEnumerable(memberState) GetMembersByState(string #state)<br/>
{<br/>
using (BulletinWizardDataContext context = DataContext)<br/>
{<br/>
IEnumerable(memberState) mems = (from m in context.Members<br/>
join ma in context.MemberAddresses<br/>
on m.UserId equals ma.UserId<br/>
join s in context.States<br/>
on ma.StateId equals s.StateId<br/>
where s.StateName == #state<br/>
select new memberState<br/>
{<br/>
userId = m.UserID,<br/>
firstName = m.FirstName,<br/>
middleInitial = m.MiddleInitial,<br/>
lastName = m.LastName,<br/>
createDate = m.CreateDate,<br/>
modifyDate = m.ModifyDate<br/>
}).ToArray(memberState)();<br/>
return mems;
}
}
The tables in my joins (Members, States, and MemberAddresses are actual tables in my Database). I created the object memberStates so I could use it in the query above (notice the Select New memberState. When the data is updated on the web page how do I persist the changes back to the Member Table? My Member Table consists of the following columns: UserId, FirstName, MiddleInitial, LastName, CreateDate, ModifyDate. I am not sure how save the changes back to the database.
Thanks,

Categories

Resources