I'm trying to insert into two releated tables with linq2sql but my code only inserts one entity(Email), I dont get any exceptions - just the other entity (attachment) is not inserted.
I think I have an mistake somewhere in the association, but i cant figure out how to set it up properly.
Thanks for help.
Insert code:
using (TransactionScope main_transaction = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromSeconds(120)))
{
foreach (var attachment in attachmets)
email.Attachments.Add(attachment);
_emails.InsertOnSubmit(email);
_context.SubmitChanges();
main_transaction.Complete();
}
Pocos:
[Table(Name = "maily")]
internal class Email
{
private EntitySet<Attachment> _attachments;
public Email()
{
this._attachments = new EntitySet<Attachment>();
}
[Column(IsPrimaryKey = true, IsDbGenerated = true, Name = "ID_mailu", AutoSync = AutoSync.OnInsert)]
public virtual int ID_mailu { get; set; }
[Association(Storage = "_attachments", OtherKey = "id_mailu")]
public ICollection<Attachment> Attachments
{
get { return _attachments.ToList(); }
set { _attachments.Assign(value); }
}
}
[Table(Name = "MailPrilohy")]
internal class Attachment
{
private EntityRef<Email> _email;
public Attachment()
{
_email = default(EntityRef<Email>);
}
[Column(IsPrimaryKey =true, IsDbGenerated = true, Name = "id_prilohy", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public int Id_Prilohy { get; set; }
[Column(Name = "id_mailu", CanBeNull = false, IsDbGenerated = true)]
public int id_mailu { get; set; }
[Association(Storage = "_email", ThisKey = "id_mailu", OtherKey = "ID_mailu", IsForeignKey = true)]
public Email Email
{
get { return _email.Entity; }
set { _email.Entity = value; }
}
}
}
So I have just solved the problem...
The problem was in several places.
1s was the association missed foreign key attribute ..
[Association(Storage = "_email", ThisKey = "id_mailu", OtherKey = "ID_mailu", IsForeignKey = true)]
public Email Email
{
get { return _email.Entity; }
set { _email.Entity = value;}
}
Next, i was supposed to insert attachments -> not the email.
_context.Attachments.InsertAllOnSubmit(attachmets);
Can you try to see if this works for you?
using (TransactionScope main_transaction = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromSeconds(120)))
{
foreach (var attachment in attachmets)
attachment.Email = email; // only this line changed
_emails.InsertOnSubmit(email);
_context.SubmitChanges();
main_transaction.Complete();
}
Related
good day ..
i created a model that has an property with [Notmapped] DataAnnotations and i created another class inherit from this model with same property but i add required DataAnnotations the problem is when i delete i got error "Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
"
My Model :
[Key]
[Display(AutoGenerateField = true, AutoGenerateFilter = true, Description = "IDDescription", GroupName = "IDGroupName", Name = "IDName", ShortName = "IDShortName", Prompt = "IDPrompt", Order = 50, ResourceType = typeof(Resources.BaseEntity))]
public long ID { get; set; }
[StringLength(207, ErrorMessageResourceName = "StringTooMuch", ErrorMessageResourceType = typeof(Resources.BaseSlider))]
[Required(AllowEmptyStrings = false, ErrorMessageResourceName = "DetailsRequired", ErrorMessageResourceType = typeof(Resources.BaseSlider))]
[Display(Name = "Description", ResourceType = typeof(Resources.BaseSlider))]
public string Description { get; set; }
[NotMapped]
public string ShortDescription
{
get
{
if (Description.Length <= 207)
{
return Description;
}
return Description.Substring(0, 207);
}
}
[Display(Name = "HasBTN", ResourceType = typeof(Resources.BaseSlider))]
public bool HasBTN { get; set; }
[Display(Name = "Is Image Dark")]
public bool IsDark { get; set; }
[Display(Name = "Link", ResourceType = typeof(Resources.BaseSlider))]
public string Link { get; set; }
[Display(Name ="Slider Type")]
public long SliderTypeID { get; set; }
[NotMapped]
//[ImageValidation(".jpg,.png,.japg", OriginalWidth = 1920, OriginalHeight = 600)]
[Display(AutoGenerateField = true, AutoGenerateFilter = true, Description = "ImagePathDescription", Name = "ImagePathName", ResourceType = typeof(Resources.BaseMore))]
public virtual HttpPostedFileBase ImagePathFile { get; set; }
#endregion
#region Relations
public virtual IList<BaseSliderPhotoUpload> Photos { get; set; }
public virtual BaseLookup SliderType { get; set; }
#endregion
public BaseSlider()
{
Photos = new List<BaseSliderPhotoUpload>();
}
and the class i created :
public class BaseSliderCreate : BaseSlider
{
#region Data
[NotMapped]
[Required]
//[ImageValidation(".jpg,.png,.japg", OriginalWidth = 1920, OriginalHeight = 600)]
[Display(AutoGenerateField = true, AutoGenerateFilter = true, Description = "ImagePathDescription", Name = "ImagePathName", ResourceType = typeof(Resources.BaseMore))]
public override HttpPostedFileBase ImagePathFile { get; set; }
#endregion
}
in delete actionresult code :
public ActionResult DeleteConfirmed(Guid id)
{
BaseSlider SliderObject = db.Sliders.Where(x => x.GUID == id && x.Deleted == null).FirstOrDefault();
SliderObject.Deleted = DateTime.Now;
SliderObject.DeletedByID = _CurrentUser.ID;
// Delete All Photos
DeletePhoto DeletePhoto = new DeletePhoto();
var DeletedPhotoName = new List<string>();
foreach (var name in SliderObject.Photos)
{
DeletedPhotoName.Add(name.FileName);
}
if (DeletePhoto.PhotoDeleted("Slider", DeletedPhotoName))
{
try
{
db.SliderPhotos.RemoveRange(SliderObject.Photos);
db.Entry(SliderObject).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception ex)
{
ErrorList.Add(ex.Message);
throw;
}
}
else
{
ErrorList.Add(DeletePhoto.ErrorMessage);
}
ViewBag.ErrorList = ErrorList;
return RedirectToAction("Delete", new { id = SliderObject.GUID });
}
when i save change i got error
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
i checked i found the EntityValidationErrors is that ImagePathFile is required..
thanks for helping my and i apologist for my bad English
I'm at wits end here. The problem is I'm trying to create a new entity in the database, with a db generated identity property, in LINQ to SQL and then create another entity associated with the first one. I'm guessing the problem is that LINQ to SQL cannot insert the second entity without the id from the first entity, which won't be known until the database generates it.
Has anyone else had this problem... if so how did you resolve it? I know I could call SubmitChanges between the creation of the first and the second entity, but this would break the transactional integrity of the program.
Here's a concrete example:
[Table(Name = "Searches")]
public class Search
{
// Db Generated Key
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public Int32 SearchID { get; set; }
// Each search can have multiple search parameters
private EntitySet<SearchParam> searchParams;
[Association(Storage = "searchParams", ThisKey = "SearchID", OtherKey = "SearchID")]
public EntitySet<SearchParam> SearchParams
{
get
{
return searchParams;
}
set
{
searchParams.Assign(value);
}
}
}
[Table(Name = "SearchParams")]
public class SearchParam
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public Int32 SearchParamID { get; set; }
[Column]
public String ParamValue { get; set; }
// Each search param is associated with one search
[Column]
public Int32 SearchID { get; set; }
private EntityRef<Search> search = new EntityRef<Search>();
[Association(Storage = "search", ThisKey = "SearchID", OtherKey = "SearchID", IsForeignKey = true)]
public Search Search
{
get
{
return search.Entity;
}
set
{
search.Entity = value;
}
}
}
So with the above code, if I were to do something like the following, .NET gives me a NullReferenceException on SubmitChanges:
using (SampleDataContext context = new SampleDataContext())
{
Search search = new Search();
search.SearchParams.Add(new SearchParam() { ParamValue = "...paramvalue..." });
context.Searches.InsertOnSubmit(search);
context.SubmitChanges();
}
It doesn't look to me like you are initializing the SearchParams list before calling add on it. Does this work:
using (SampleDataContext context = new SampleDataContext()) {
Search search = new Search();
search.SearchParams = new EntitySet<SearchParam>(); //<-- THIS LINE
search.SearchParams.Add(new SearchParam() { ParamValue = "...paramvalue..." });
context.Searches.InsertOnSubmit(search);
context.SubmitChanges();
}
Don't know why yours is not working, but this should work
using (SampleDataContext context = new SampleDataContext())
{
Search search = new Search();
SearchParam param = new SearchParam() { ParamValue = "...paramvalue..." })
param.search = search
context.searchparams.InsertOnSubmit (param ) // Not sure if this is necessary
context.Searches.InsertOnSubmit(search);
context.SubmitChanges();
}
And all in one transaction.
Try adding the following to your Search class
public Search()
{
this.SearchParams = new EntitySet<SearchParams >(
new Action<SearchParams >(this.attach_SearchParams ),
new Action<SearchParams >(this.detach_SearchParams ));
}
private void attach_SearchParams(SearchParams sp)
{
sp.Search = this;
}
private void detach_Search(SearchParams sp)
{
sp.Search = null;
}
I'm trying to set up a one to many relationship with linq on Windows Phone 8.
My problem is that in the EntitySet<> field only one of the classes which should be stored, really gets stored to the database.
So i made a simple Project to give you clue of my problem.
There are two classes Person and Number. One Person can have many numbers.
Here is the number class:
[Table]
class Number
{
[Column(IsPrimaryKey = true, IsDbGenerated = true)]
public int NumberID { get; set; }
[Column]
public int PhoneNumber { get; set; }
}
And the Person class:
[Table]
class Person
{
private EntitySet<Number> numbers = new EntitySet<Number>();
[Column(IsPrimaryKey = true, IsDbGenerated = true)]
public int PersonID { get; set; }
[Column]
public string Name { get; set; }
[Association(Storage = "numbers", ThisKey = "PersonID", OtherKey = "NumberID")]
public EntitySet<Number> Numbers
{
get { return numbers; }
set
{
numbers.Assign(value);
}
}
}
When i now try to insert a person with three numbers:
EntitySet<Number> numbers = new EntitySet<Number>();
DataBase db = new DataBase(App.DBConnectionString);
Number num1 = new Number() { PhoneNumber = 111111 };
Number num2 = new Number() { PhoneNumber = 222222 };
Number num3 = new Number() { PhoneNumber = 333333 };
Person person = new Person() { Name = "Donald" };
numbers.Add(num1);
numbers.Add(num2);
numbers.Add(num3);
person.Numbers = numbers;
try
{
db.Persons.InsertOnSubmit(person);
db.SubmitChanges();
}
catch (Exception s)
{
// do nothing
}
And then try to retrieve the data again:
DataBase db = new DataBase(App.DBConnectionString);
string text = "";
foreach (Person person in db.Persons)
{
foreach (Number num in person.Numbers)
{
text += num.PhoneNumber + System.Environment.NewLine;
}
}
TextBlock.Text = text;
i only get the first phone number which is '111111'. Interesting is, that on the second run the text string holds '111111' and '222222'. This is because in the second Person the second phone number is saved and in the third person the third phone number and so on.
So if you do that often enough you get:
111111
222222
333333
111111
222222
333333
...
Btw if you jump with the debugger to 'db.Persons.InsertOnSubmit(person);' and look into 'person' there is a list with all three numbers in it. So it should work...
I tried really hard, but i can't figure out a way to get it right. My suspicion though is on the [Association] attribute.
Here is a link to the source so you can get the whole picture if you want: https://docs.google.com/file/d/0B5G4zya_BlZyUlU0azVvbVdiajA/edit?usp=sharing
I also had that problem a while ago. I added a few lines to my code that made it disappear.
Linq2SQL is a bit magical and sometimes things (don't) work for no obvious reason. Just to be perfectly sure you might want to implement the official One-To-Many solution provided by Microsoft:
http://msdn.microsoft.com/en-us/library/vstudio/bb386950(v=vs.100).aspx
Also I would make the following adjustments:
[Table]
class Number
{
[Column(IsPrimaryKey = true, IsDbGenerated = true)]
public int NumberID { get; set; }
[Column]
public int _personID { get; set; }
[Column]
public int PhoneNumber { get; set; }
private EntityRef<Person> _person;
[Association(Storage = "_person", ThisKey = "_personID", OtherKey = "PersonID", IsForeignKey = true)]
public Person Person
{
get { return this._person.Entity; }
set {
this._person.Entity = value;
if (value != null)
{
this._personID = value.PersonID;
}
}
}
}
And I'd add this to the constructor:
public Person()
{
this._numbers = new EntitySet<Number>(
delegate (Number entity)
{
entity.Person = this;
},
delegate (Number entity)
{
entity.Person = null;
});
}
As a test I would not try to load the entire object. First just check if all numbers have made it to the database:
var numbers = db.Numbers.ToList();
This is because L2S on Windows Phone has serious trouble reading deep object connections. One level is fine but deeper relationships are ignored. So if your Person class resides in another class that could be a problem as well.
Edit: You can add options to your DB context in order to force deep object loading:
_db = new DBContext("isostore:/mydb.sdf");
DataLoadOptions loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Person>(p => p.Numbers);
_db.LoadOptions = loadOptions;
I got one entity linked with another:
[Table(Name = "Employees")]
public sealed class Employee
{
[Column(Name = "Id", UpdateCheck = UpdateCheck.Never, IsPrimaryKey = true, IsDbGenerated = true, DbType = "Int NOT NULL IDENTITY")]
public int Id { get; set; }
[Column(Name = "Phone", UpdateCheck = UpdateCheck.Never, DbType = "Char(20)")]
public string Phone { get; set; }
[Column(Name = "UserId", UpdateCheck = UpdateCheck.Never, DbType = "Int NOT NULL")]
public int UserId { get; set; }
[Association(Storage = "_user", ThisKey = "UserId")]
public User User
{
get { return _user.Entity; }
set { _user.Entity = value; }
}
private EntityRef<User> _user;
}
[Table(Name = "Users")]
public sealed class User
{
[Column(Name = "Id", UpdateCheck = UpdateCheck.Never, IsPrimaryKey = true, IsDbGenerated = true, DbType = "Int NOT NULL IDENTITY")]
public int Id { get; set; }
[Column(Name = "LastName", UpdateCheck = UpdateCheck.Never, DbType = "NVarChar(100)")]
public string LastName { get; set; }
[Column(Name = "FirstName", UpdateCheck = UpdateCheck.Never, DbType = "NVarChar(100)")]
public string FirstName { get; set; }
}
Then I want to create an employee:
var entity = new Employee
{
Phone = "Some",
User = new User
{
FirstName = "Some",
LastName = "Some"
}
};
context.GetTable<Employee>().InsertOnSubmit(entity);
context.SubmitChanges();
And got this:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Employees_Users"
Is it posible to create linked entities in LINQ to SQL?
Try specifying the OtherKey property of the AssociationAttribute as well. If not specified, it will default to the key of the related class, but in your case the names of the keys in the different classes are different (UserId vs Id).
[Association(Storage = "_user", ThisKey = "UserId", OtherKey="Id")]
public User User
{
get { return _user.Entity; }
set { _user.Entity = value; }
}
private EntityRef<User> _user;
This part of code works fine
[Association(Storage = "profile", ThisKey = "UserId", OtherKey = "UserId")]
public Profile User {
get { return this.profile.Entity; }
set { this.profile.Entity = value; }
}
but if i add in this class
System.ComponentModel.DataAnnotations
then, Association could not be found.
Where is the problem ?
An enhancement to Austin's answer is to employ the using statement:
using L2SAssociation = System.Data.Linq.Mapping.Association;
[L2SAssociation(Storage = "profile", ThisKey = "UserId", OtherKey = "UserId")]
public Profile User
{
get { return this.profile.Entity; }
set { this.profile.Entity = value; }
}
It looks like you've got two namespaces that are conflicting. Try changing Association to System.Data.Linq.Mapping.Association, so it would look like:
[System.Data.Linq.Mapping.Association(Storage = "profile", ThisKey = "UserId", OtherKey = "UserId")]
public Profile User {
get { return this.profile.Entity; }
set { this.profile.Entity = value; }
}