Entity Framework 5 doesn't update Many to Many relation - c#

I have the following tables in an SQL2008 database:
Accommodation
code varchar(18)
name varchar(80)
This table has more columns but I have removed them here for simplicity.
Attributes
code int
name varchar(50)
AccommodationAttributes
AccommodationCode varchar(18)
AttributeCode int
As you may get, AccommodationAttributes describes the many to many relationship between Accommodations and Attributes.
I have created my model (EF5) using database first, and it has created two classes linked with a navigation property.
All this seems correct.
What I am trying to do is add values in the db, but though I am able to add Accommodations and Attributes, I don't seem to be able to make it add the corresponding values in the AccommodationAttributes table.
I am reading from an XML file.
EDIT
Below is the code I am using exactly as it is:
public static void UpdateAccommodation(string file)
{
InterHomeEntities ih = new InterHomeEntities();
Stopwatch sw = new Stopwatch();
ih.Configuration.AutoDetectChangesEnabled = false;
ih.Configuration.ValidateOnSaveEnabled = false;
ih.Configuration.LazyLoadingEnabled = false;
XElement xe = XElement.Load(file);
DateTime DayToProcess = DateTime.Now.AddDays(Properties.Settings.Default.InterHome_DaysToProcess);
var Attributes = xe.XPathSelectElements("//attribute").Select(x => x.Value).Distinct();
foreach (var attribute in Attributes)
{
Attribute at = ih.Attributes.Where(x => x.name == attribute).SingleOrDefault();
bool newEntry = at == null ? true : false;
at = newEntry ? new Attribute { name = attribute } : at;
ih.Attributes.Attach(at);
ih.Entry(at).State = newEntry ? System.Data.EntityState.Added : System.Data.EntityState.Modified;
ih.SaveChanges();
}
var Accommodations = from c in xe.Elements("accommodation") select c;
int AccomodationCount = Accommodations.Count();
int AccomodationIndex = 0;
foreach (var accommodation in Accommodations)
{
AccomodationIndex++;
var AccCode = accommodation.Element("code").Value;
try
{
Accommodation a = ih.Accommodations.Where(x=>x.code == AccCode).SingleOrDefault();
bool newAccommodation = a == null ? true : false;
a = !newAccommodation ? a :
new Accommodation
{
code = accommodation.Element("code") == null ? null : accommodation.Element("code").Value,
name = accommodation.Element("name") == null ? null : accommodation.Element("name").Value,
country = accommodation.Element("country") == null ? null : accommodation.Element("country").Value,
region = accommodation.Element("region") == null ? null : accommodation.Element("region").Value,
place = accommodation.Element("place") == null ? null : accommodation.Element("place").Value,
zip = accommodation.Element("zip") == null ? null : accommodation.Element("zip").Value,
type = accommodation.Element("type") == null ? null : accommodation.Element("type").Value,
quality = accommodation.Element("quality") == null ? (byte?)null : Convert.ToByte(accommodation.Element("quality").Value),
details = accommodation.Element("details") == null ? null : accommodation.Element("details").Value,
brand = accommodation.Element("brand") == null ? null : accommodation.Element("brand").Value,
pax = accommodation.Element("pax") == null ? (double?)null : Convert.ToDouble(accommodation.Element("pax").Value),
sqm = accommodation.Element("sqm") == null ? (double?)null : Convert.ToDouble(accommodation.Element("sqm").Value),
floor = accommodation.Element("floor") == null ? (double?)null : Convert.ToDouble(accommodation.Element("floor").Value),
rooms = accommodation.Element("rooms") == null ? (double?)null : Convert.ToDouble(accommodation.Element("rooms").Value),
bedrooms = accommodation.Element("bedrooms") == null ? (double?)null : Convert.ToDouble(accommodation.Element("bedrooms").Value),
toilets = accommodation.Element("toilets") == null ? (double?)null : Convert.ToDouble(accommodation.Element("toilets").Value),
bathrooms = accommodation.Element("bathrooms") == null ? (double?)null : Convert.ToDouble(accommodation.Element("bathrooms").Value),
lat = accommodation.Element("geodata") == null || accommodation.Element("geodata").Element("lat") == null ? null : accommodation.Element("geodata").Element("lat").Value,
lng = accommodation.Element("geodata") == null || accommodation.Element("geodata").Element("lng") == null ? null : accommodation.Element("geodata").Element("lng").Value,
LastUpdated = DateTime.Now
};
foreach (var attribute in accommodation.Elements("attributes").Elements("attribute").Select(x=>x.Value))
{
Attribute at = ih.Attributes.Where(x => x.name == attribute).SingleOrDefault();
a.Attributes.Add(at);
}
if (newAccommodation)
{
ih.Accommodations.Add(a);
}
else
{
ih.Entry(ih.Accommodations.Where(x => x.code == a.code).SingleOrDefault()).CurrentValues.SetValues(a);
ih.Entry(ih.Accommodations.Where(x => x.code == a.code).SingleOrDefault()).State = System.Data.EntityState.Modified;
}
ih.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
ih.SaveChanges();
}
After running this code I run the following in SQL:
select COUNT(*) from Accommodations
select COUNT(*)from Attributes
select COUNT(*)from AccommodationAttributes
But though I see entries in the two tables, the link table comes with 0 rows.
I have tried other variations, like attaching the objects to the context, or implicitly specifying that it is a modified object.
By the time that this code will run I am sure that the Attributes are already inserted in the db, but the Accommodation is either an Insert or Update.
UPDATE
After further investigation, it seems that it works when I add a new Accommodation, but it fails when the Accommodation is already in the db and I just add new attributes. In my case in the process of developing I had first added the Accommodation and in a later step of development I created the process to import attributes. So I need to find a way to update the relationship when both accommodation and attribute are already in the db.
I am eager to hear your thoughts,
Giannis

make sure you set the following:
In table Accomodation PK is code.
In table Attrrrribute PK is code.
In table AccomodationAtrribute PK is AccomodationCode+AttributeCode.
In table AccomodationAttribute set a foriegn key of AccomondationCode to colum code in table Accomodation.
In table AccomodationAttribute set a foriegn key of AttributeCode to colum code in table Attribute.
also for the linking table to be filled you need to link an attribute instance to an accomodation or vice versa in the code. somtheing like:
accomodation.Attrbutes.Add(attribute);

Related

C# DataContext returns null on comparing dates

I am creating a database based desktop application and stuck in a problem..Actually I am new to Linq...
if (OP == "Delete")
{
tb = DB.TblPurchase.FirstOrDefault(e => e.PID.Equals(ID) || e.PItemName.Equals(ItemName) || e.PCategory.Equals(Category) || e.PDate.Equals(Date)); // returns null on comparing date
DB.Delete(tb);
return "Deleted";
}
else if (OP == "Search")
{
tb = DB.TblPurchase.FirstOrDefault(e => e.PID.Equals(ID) || e.PItemName.Equals(ItemName) || e.PCategory.Equals(Category) || e.PDate == Date); // returns null on comparing date as well
return tb;
}
Starting of the function :
`public object AUDS_Purchase(string OP, int? ID = null, string ItemName = "",string Category = "", string Supplier = "", DateTime? Date = null, int ? Rate = null, int? Quantity = null, int? Total = null)
{
using(var DB = new MYDBDB())
{
TblPurchase tb = null;`
Calling the function :
`dynamic _ID = Cmb_Search_ItemID.SelectedIndex == -1 ? null : Cmb_Search_ItemID.SelectedValue;
dynamic _ItemName = Cmb_Search_ItemName.SelectedIndex == -1 ? null : Cmb_Search_ItemName.SelectedValue.ToString();
dynamic _ItemCategory = Cmb_Search_ItemCategory.SelectedIndex == -1 ? null : Cmb_Search_ItemCategory.SelectedValue.ToString();
dynamic _Date = Search_Item_Date;
dynamic res;
if (Search_Item_Date.Checked == false)
{
if (_ID != null || _ItemName != null || _ItemCategory != null)
{
res = tb.AUDS_Inventory("Search", Convert.ToInt32(_ID), _ItemName, _ItemCategory);
tb.ShowNewData(metroGrid1, res);
}
else
{
MetroMessageBox.Show(this, "No Value Given\nSelect a value", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
else
{
res = tb.AUDS_Inventory("Search", Convert.ToInt32(_ID), _ItemName, _ItemCategory, Date:_Date);
tb.ShowNewData(metroGrid1, res);
}`
Note : I am sure tb is null because I used breakpoint...
My table contains the records : Picture prove
Everything works fine with the database and the table...
This is my first post BTW..
Thanks In Advance :)
If the Date you are providing in the function contains different time then that might be the cause. As I can see from your database picture you are trying to compare date only. So you can try excluding time, it might work. You can try
e.PDate.Date == Date.Date in your function.

Need help in reducing the Cyclomatic complexity of LINQ query used for constructing JSON string

I have got 5 different sources of data(request, lsitCC, listSEDI, listSEDIFees and XMLRoot loaded into respective C# Array list objects). I need to construct a JSON request by combining the data from all of those sources based on certain conditions. I have written below code in C# using NewtonSoft.JSON. The cyclomatic complexity for this LINQ query is coming around 40 which is at a higher side. Could anyone please advice on how the complexity can be reduced. Thanks in advance.
I presume the query is pretty user readable, Please let me know if inline comments are required.
var input = from RequestNode in request
select new
{
Documents = (from objCC in lsitCC
where objCC.ID == RequestNode.ID
select new
{
Request = (from objSEDI in listSEDI
where objSEDI.ID == objCC.ID && RequestNode.POSTAL.Count(p => p.PID == objSEDI.PID) > 0
join Config in RequestNode.POSTAL on objSEDI.PID equals Config.PID
select new
{
ReqItem = (Config.ReqItem == null) ? "" : Config.ReqItem,
Code = (RequestNode.Code == null) ? "" : RequestNode.Code,
Camp = (RequestNode.Camp == null) ? "" : RequestNode.Camp,
CCT = new
{
ID = (objCC.ID == null) ? "" : objCC.ID,
Band = (RequestNode.Band == null) ? "" : RequestNode.Band,
Context = (RequestNode.Context == null) ? 0 : RequestNode.Context,
IsActive = (RequestNode.IsActive == null) ? false : RequestNode.IsActive,
MaxLimit = (objCC.MaxLimit == null) ? 0 : objCC.MaxLimit,
MinLimit = (objCC.MinLimit == null) ? 0 : objCC.MinLimit
},
User = RequestNode.User,
POSTAL = new
{
PID = (objSEDI.PID == null) ? "" : objSEDI.PID,
Type = (Config.Type == null) ? "" : Config.Type,
Amount = (Config.Amount == null) ? 0 : Config.Amount,
IsValid = (Config.IsValid == null) ? false : Config.IsValid,
Code = (Config.Code == null) ? "" : Config.Code,
Infos = new
{
Info = (from objRoot in XMLRoot
where objRoot.ID == objCC.ID && objRoot.Channel == "Channel1" && objRoot.Group == "GROUP_1" && objRoot.Code == Config.Type.Substring(0, 3) && objRoot.PIDCode == Config.Type.Substring(3, 1)
select new
{
InfoFrom = (objRoot.InfoFrom == null) ? "" : objRoot.InfoFrom,
Selection = (objRoot.Handling == null) ? "" : objRoot.Selection,
Rate = (objRoot.Rate == null) ? "" : objRoot.Rate
})
},
POSTALFee = from objSEDIFee in listSEDIFees
where objSEDIFee.ID == objCC.ID && objSEDIFee.PID == objSEDI.PID
select new
{
BaseValue = (objSEDIFee.BaseValue == null) ? 0 : objSEDIFee.BaseValue,
UpdatedValue = (objSEDIFee.UpdatedValue == null) ? 0 : objSEDIFee.UpdatedValue,
BaseType = (objSEDIFee.BaseType == null) ? "" : objSEDIFee.BaseType,
UpdatedType = (objSEDIFee.UpdatedType == null) ? 0 : objSEDIFee.UpdatedType
},
OutputRoot = new
{
Output = from output in outputroot
select new
{
Type = 0,
SubType = 0,
OutputReference = 0
}
}
},
})
})
};
var streamRead = JsonConvert.SerializeObject(input, Newtonsoft.Json.Formatting.Indented);
This seems like simply a case of adding the appropriate amount of translators to your code base. Yes Translators are tedious, but if you feel you have too much logic in one query here, I suggest that could be the way for you to go.
This will mean that you will have to either ditch the anon types, or embrace the dynamic keyword (would that even work?!)
You may also need to look at some sort of build pattern or intermediate state.
On further inspection, it appears that you introduce some global variable halfway down the query e.g. XMLRoot & listSEDIFees. It might be nicer if this was more explicit. You could also cut down on excessive work by pre filtering XMLRoot with the static part of your where clause (objRoot.Channel == "Channel1" && objRoot.Group == "GROUP_1") instead of re-running that each time. Maybe something like
var channel1Group1Info = XMLRoot.Where(objRoot=>objRoot.Channel == "Channel1" && objRoot.Group == "GROUP_1").ToArray();
The rest I would just pick off one piece at a time, reducing the amount of work this query is doing.

Due to LINQ Retrieving of Record data's i have this error: NullReferenceException was unhandled by user code

private void getUserLoginDepartment(string AccessID, string UserPROFid)
{
try
{
DBWPAccountRecordsDataContext DBACCOUNT = new DBWPAccountRecordsDataContext();
var query = (from i in DBACCOUNT.WP_UserAccessPorts
join
z in DBACCOUNT.WP_Departments on i.AccessPortID equals z.Dept_ID
where i.AccessPortID == AccessID && i.ProfileUser_ID == UserPROFid
select new
{
PORT1 = i.AccessPoint1,
PORT2 = i.AccessPoint2,
PORT3 = i.AccessPoint3,
PORT4 = i.AccessPoint4,
DEPT = z.Dept_DESC,
DEPTPORT = z.Dept_PortNo
}).FirstOrDefault();
if (query.PORT1.ToString() != null || query.PORT1.ToString() != string.Empty)
{ Session["Port1"] = query.PORT1; }
else { Session["Port1"] = ""; }
if (query.PORT2.ToString() != null || query.PORT2.ToString() != string.Empty)
{ Session["Port2"] = query.PORT2; }
else { Session["Port2"] = ""; }
if (query.PORT3.ToString() != null || query.PORT3.ToString() != string.Empty)
{ Session["Port3"] = query.PORT3; }
else { Session["Port3"] = ""; }
if (query.PORT4.ToString() != null || query.PORT4.ToString() != string.Empty)
{ Session["Port4"] = query.PORT4; }
else { Session["Port4"] = ""; }
}
finally
{
}
}
The Error occures when i reach break point 1st IF Statement the record on my database shows that its not empty which its value is "WebAdmin" but then suppost to be it should pick it up and store it to the Session["PORT1"] that i have made is there something i missed or i'm doing it wrong on my linq Query. NOTE:*This is an ASP.NET C# Application
EDIT 10/2/2013 0420PM:
It's still an Error After using that method sir.
1) you should check query for null when you use FirstOrDefault
2) you need to check each PORTX for null
3) use string.IsNullOrEmpty( ) to check if the string of PORTX is null
var query = ( ... ).FirstOrDefault( );
if( query != null )
{
if( query.PORT1 != null && !string.IsNullOrEmpty( query.PORT1.ToString( ) ) )
{
}
else { ... }
}
You have to check query.PORT1 for null before calling ToString on it, you can use String.IsNullOrEmpty to check both conditions. Before checking query.PORT1 you need to check if query is null or not. You also need to use && instead of or operator as || will cause the right side of or operator to be evaluated if left is false and on right side calling ToString on null will again through exception.
if (query != null && query.PORT1 != null && query.PORT1.ToString() != string.Empty)
{ Session["Port1"] = query.PORT1; }
Using IsNullOrEmpty
if(query != null && !String.IsNullOrEmpty(query.PORT1))
{
Session["Port1"] = query.PORT1;
}

linq conditional query

What would be the best practice for setting a status depending on several other "columns" retrieved in a linq query.
var result = (from q in query
select new Item
{
ApprovedDate = q.ApprovedDate,
CreatedDate = q.CreatedDate,
DeclinedDate = q.DeclinedDate,
Status = 0
});
I'd like to set the status to either 0, 1, 2.
(ApprovedDate == null and DeclinedDate == null) --> 0
(ApprovedDate != null and DeclinedDate == null) --> 1
(DeclinedDate != null) --> 3
So perhaps something like:
var result = (from q in query
select new Item
{
ApprovedDate = q.ApprovedDate,
CreatedDate = q.CreatedDate,
DeclinedDate = q.DeclinedDate,
Status = (q.CreatedDate == null && q.DeclinedDate == null) ? 0 : (q.ApprovedDate != null && q.DeclinedDate == null) ? 1 : 2
});
I might add even more status combinations, so should I try and do this in the linq select query, in my repository object.. Or later on in the controller where I would do a .ToList() and then foreach the list to set the correct status code?
Having even more than 3 statuscodes, the linq query gets "hard" to read.
What about moving status calculation to Item class? If status property depends on other properties value, then it's definitely calculated property:
var result = from q in query
select new Item
{
ApprovedDate = q.ApprovedDate,
CreatedDate = q.CreatedDate,
DeclinedDate = q.DeclinedDate
});
And
public class Item
{
// other properties
public int Status
{
get
{
if (ApprovedDate == null and DeclinedDate == null)
return 0;
if (ApprovedDate != null and DeclinedDate == null)
return 1;
if (DeclinedDate != null)
return 3;
// etc
}
}
}
Actually I think it's best option, because in this case status calculation logic will be close to required data. If (for some reason) you can't use this approach, then move setting statuses to local items collection:
var items = result.ToList().ForEach(i => i.Status = CalculateStatus(i));
Maybe wrapped all in a function An do a linq like this
var result = (from q in query sele q).AsEnumerable()
.Select( x => new Item()
{
ApprovedDate = x.ApprovedDate,
CreatedDate = x.CreatedDate,
DeclinedDate = x.DeclinedDate,
Status = MyStatusFunction(x.CreatedDate,q.DeclinedDate)
});
public int MyStatusFunction(DateTime ApprovedDate , Datetime DeclinedDate)
{
if (ApprovedDate == null and DeclinedDate == null) return 0;
else if(ApprovedDate != null and DeclinedDate == null) return 1;
else if (DeclinedDate != null) return 3;
}

C# XML ToList inside ToList

I have the following C# code and I have no idea why it's not working (I'm getting a NullReferenceException error). If I define Recipe as new List() everything starts working OK.
foreach (XElement element in document.Descendants("vegetables"))
{
VegetablesList = (
from vegetables in element.Elements()
select new FoodItem()
{
Name = (vegetables.Element("name") == null) ? null : vegetables.Element("name").Value.ToString(),
Bcg = (vegetables.Element("bcg") == null) ? null : vegetables.Element("bcg").Value.ToString(),
Info = (vegetables.Element("info") == null) ? null : vegetables.Element("info").Value.ToString(),
Recipes = (
from recipes in element.Element("recipes").Elements()
select new Recipe()
{
Name = (recipes.Element("name") == null) ? null : recipes.Element("name").Value.ToString(),
Text = (recipes.Element("text") == null) ? null : recipes.Element("text").Value.ToString()
}
).ToList()
}
).ToList();
VegetablesListBox.ItemsSource = VegetablesList;
}
Thanks for your help!
My guess is that element.Element("recipes") returns null, which means that the recipes element does not exist for that iteration.

Categories