Linq C# struggling with update code - c#

if anyone can explain to me what is wrong with this I would really appreciate it because its not throwing an exception so I can't see why its not updating the record.
Sorry I should of said I am trying to update an existing record in a database, "CompName" was used as the primary key, this isn't my database design or application or I would of used an int as a id and coded things a lot differently.
using (CompanyAndContactDataContext dc = new CompanyAndContactDataContext())
{
try {
Company c = (from datavalue in dc.Companies
where datavalue.CompanyName == CompName
select datavalue).First();
c.CompanyName = txtEditCompanyName.Text;
c.CompanyEmailAddress = txtEditCompanyEmail.Text;
c.CompanyTelephoneNumber = txtEditCompanyTelephone.Text;
c.CompanyFaxNumber = txtEditCompanyFax.Text;
c.CompanyAddress1 = txtEditAddress1.Text;
c.CompanyAddress2 = txtEditAddress2.Text;
c.CompanyAddress3 = txtEditAddress3.Text;
c.CompanyAddress4 = txtEditAddress4.Text;
c.PostCode = txtEditPostcode.Text;
dc.SubmitChanges();
} catch (Exception exep) {
}
}

At first guess, it's probably the First() call that's throwing the exception. You could make it FirstOrDefault, or try to pass an Id to your method (Make them select a value from a dropdown or auto complete, then get the Id of what they selected). That way you can be sure when this code hits you can update the entry.
If you pass an Id to the method you can even use "Single" or "SingleOrDefault"
Edit:
Say you enter "MyCompany" as CompName, and this doesn't exist in the Database table. If you call First() it'll throw an exception. If you call FirstOrDefault() it'll return "c" as null.
If you call "c.CompanyName" or anything else with a dot, it'll return an exception.
You need to check if c is null before calling any dot methods.

Related

C# : how to declare a variable for an entity table?

I'm using C# Entity Framework to select records from a database table. Based on selection criteria, I will use different select statements.
if ( condition1 )
{
var records = _context.Member.Select( .... )
}
else if (condition2)
{
var records = _context.Member.Select( .....)
}......
And then, I need to make some decisions depending on whether there are records and process those records.
if (records != null)
{
}
else if (....)
The compiler complains that "records" does not exist in the current context. I think the reason why this happens is that records is declared in a if block. I don't want to do the 2nd step inside the if block because the processing is the same and it quite lengthy.
I've tried declaring record outside the block first, but I don't know what Type to use. So how do I declare a variable to hold the records return from Entity Framework?
Thanks.
Edit: After reading the comments and Answer. I think I know where my confusion lies. If my select is a new anonymous object, what should my type be ?
var records = _context.Member
.Select(x => new {Name = x.name, Address = x.address} );
When I hover over the Select, it says:
Returns: An IQueryable<out T> that contains elements from the input sequence that satisfy the condition specified by predicate.
Types: ‘a is new { .... }
As it's an anonymous object, what should I state as the type for it ?
Thanks again for the great help.
What data type is records? Find that out and lets call that T.
If you are using Visual Studio, just hover over the Select method. It will popup some information about method, there is also a return type before the name of the method.
Then write this code:
T records = null; // or some kind of empty collection
if ( condition1 )
{
records = _context.Member.Select( .... )
}
else if (condition2)
{
records = _context.Member.Select( .....)
}
The reason you ran in to this problem is that 'records' is defined only in the scope of '{}' curly braces. By bringing it up like i showed you, you move it to the same context where you have the decisions
if (records != null)
{
}
else if (....)

LinqPad conversion error

i am getting an error on DuplicateOrder(TestOrder) as "cannot convert from tables.Order to int" even though the OrderID in the Orders table is an int
void DuplicateOrder(int orderId)
{
string methodName = MethodBase.GetCurrentMethod().Name;
try {
using (MSBLegacyContext db = new MSBLegacyContext())
{
var TestOrder = db.Orders.Where(i=>i.OrderID == orderId).FirstOrDefault();
DuplicateOrder(TestOrder);
}
}
catch (Exception ex)
{
Console.WriteLine(methodName, ex, string.Format("Test", orderId));
}}
what am i missing here?
Root cause, the FirstOrDefault in below line will return the either first order object or null.
var TestOrder = db.Orders.Where(i=>i.OrderID == orderId).FirstOrDefault();
So type of testOrder is order, but the below method call is expecting the int parameter.
DuplicateOrder(TestOrder);
That's the reason yor are getting -
cannot convert from tables.Order to int
To fix the issue, you need to add Select query in the Linq query and select the int column, something like below -
var TestOrder = db.Orders.Where(i=>i.OrderID == orderId).Select(s=> s.YourIntColumn).FirstOrDefault();
Edit:
After looking at you entire method and comments from others, you method call will definitely go to infinite loop. As you have not mentioned the purpose of this code, I am just assuming that you want to see whether a particular orderid is duplicate in the database or not. For that you can just use Count query and return the Boolean value. (not sure why do you have void in your method signature)
bool DuplicateOrder(int orderId) // changed from void to bool
{
using (MSBLegacyContext db = new MSBLegacyContext())
{
return db.Orders.Where(i=>i.OrderID == orderId).Count()>1; // returns true if more than 1 order found
}
}
If you have coded it correctly in your code, and if for a given orderId there were an Order, then your method would cause an infinite loop. Having said that, below is the correct way to get the int OrderId value:
using (MSBLegacyContext db = new MSBLegacyContext())
{
var TestOrder = db.Orders
.FirstOrDefault(i=>i.OrderID == orderId);
if (TestOrder != null)
{
var orderId = TestOrder.OrderId;
// ...
}
// else whatever you would do
}
If OrderId is a primary key, then you would use SingleOrDefault() instead of FirstOrDefault(). The difference is, if there is more than 1 entry in the database with given orderId, SingleOrDefault() would throw an error - and in the case of primary keys that is what you want.

Linq to SQL InvalidCastException

this is somewhat tricky to figure out I think, perhaps I am missing something.
I am a newbie trying to rig a database mapped via Linq-to-SQL to my server. There is a function called by clients which retrieves UserAccount from the database:
public static explicit operator Dictionary<byte, object>(UserAccount a)
{
Dictionary<byte, object> d = new Dictionary<byte, object>();
d.Add(0, a.AuthenticationDatas.Username);
int charCount = a.Characters.Count;
for (int i = 0; i < charCount; i++)
{
d.Add((byte)(i + 1), (Dictionary<byte, object>)a.Characters[i]);
}
return d;
}
What this actually does is convert a UserAccount type to my server datatype of Dictionary. UserAccount itself is retrieved from database then converted via this function.
However when I run this function, I get InvalidCastException on line:
int charCount = a.Characters.Count;
Moreover, when VS breakpoints # this line, I can wait a few seconds and proceed and the excpetion will be gone! It retrieves Characters.Count correctly after that.
Here is my Characters mapping:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="UserAccount_Character", Storage="_CharactersTBs", ThisKey="UID", OtherKey="UID")]
public EntitySet<Character> Characters
{
get
{
return this._Characters;
}
set
{
this._Characters.Assign(value);
}
}
I believe whats happening is that request is somehow executed on another thread then the one that interacts with database, and it errors out before database can actually retrieve Characters table. I am not quite sure...
Does anyone know what the problem might be and how can I syncronize it (without adding some gimp delay)?
EDIT:
Ok I narrowed down the problem. It has nothing to do with different threads networking or what not... Its just me being stupid. Here is a simple databse query which throws InvalidCastException # line int count = UA.Characters.Count;
static void Main(string[] args)
{
IEnumerable<UserAccount> query = from p in PBZGdb.Instance.AuthenticationDatas
where p.Username == "Misha" && p.Password == "123"
select p.UserAccount;
UserAccount UA = query.ElementAt(0);
int count = UA.Characters.Count;
Console.WriteLine(count);
Console.ReadKey();
}
(p.s.) UA is NOT null it indeed finds a correct instance of userAccount and it has 2 Characters. If I wait few seconds and try again exception goes away..
What am I doing wrong? This is the first time I really use a database in VS please help! :)
It looks like you are running in to a problem with the deferred execution of the EntitySet. A simple way to check this and potentially work around it will be to try calling the .Count() method, instead of accessing the .Count property.
You could have a look in the debugger as soon as you hit that line, and look at the value of a.Characters.IsDeferred also.
edit
Another thing you could try would be to force execution of the query by implicitly calling it's .GetEnumerator() (and associated .MoveNext()) by replacing your loop with a foreach:
int i = 0;
foreach (var character in a.Characters)
{
d.Add( /* ... */ );
++i;
}
double edit
removed commentary about
d.Add((byte)(i + 1), (Dictionary<byte, object>)a.Characters[i]);
after clarification in the comments below
Hey just want anyone having the same problem know, I figured it out. What happened was I manualy renamed LINQ .dbml file when I added it to my project after it was geneerated by sqlmetal. And of course I did it inconsistently (it was renamed in designer but not in its .cs file). I just re-generated a new .dbml file with sqlmetal with a correct name this time and everything works like butter!
Thanks guys!

EF4.1 DBContext: Insert/update in one Save() function without identity PK

I have a streets table, which has a combo of two string columns acting as the PK, being postalcode and streetcode.
With EF4.1 and DBContext, I'd like to write a single "Save" method than takes a street (coming in in an unattached state), checks if it already exists in the database. If it does, it issues an UPDATE, and if it doesn't, it issues an INSERT.
FYI, the application that saves these streets, is reading them from a textfile and saves them (there a few tens of thousands of these "streetlines" in that file).
What I've come up with for now is:
public void Save(Street street)
{
var existingStreet = (
from s in streetContext.Streets
where s.PostalCode.Equals(street.PostalCode)
&& s.StreetCode.Equals(street.StreetCode)
select s
).FirstOrDefault();
if (existingStreet != null)
this.streetContext.Entry(street).State = System.Data.EntityState.Modified;
else
this.streetContext.Entry(street).State = System.Data.EntityState.Added;
this.streetContext.SaveChanges();
}
Is this good practice ? How about performance here ? Cause for every street it first does a roundtrip to the db to see if it exists.
Wouldn't it be better performance-wise to try to insert the street (state = added), and catch any PK violations ? In the catch block, I can then change the state to modified and call SaveChanges() again. Or would that not be a good practice ?
Any suggestions ?
Thanks
Select all the streets then make a for each loop that compares and change states. After the loop is done call saveChanges. This way you only make a few calls to the db instead of several thousends
Thanks for the replies but none were really satisfying, so I did some more research and rewrote the method like this, which satisfies my need.
ps: renamed the method to Import() because I find that a more appropriate name for a method that is used for (bulk) importing entities from an outside source (like a textfile in my case)
ps2: I know it's not really best practice to catch an exception and let it die silently by not doing anything with it, but I don't have the need to do anything with it in my particular case. It just serves as a method to find out that the row already exists in the database.
public void Import(Street street)
{
try
{
this.streetContext.Entry(street).State = System.Data.EntityState.Added;
this.streetContext.SaveChanges();
}
catch (System.Data.Entity.Infrastructure.DbUpdateException dbUex)
{
this.streetContext.Entry(street).State = System.Data.EntityState.Modified;
this.streetContext.SaveChanges();
}
finally
{
((IObjectContextAdapter)this.streetContext).ObjectContext.Detach(street);
}
}
Your code must result in exception if street already exists in the database because you will load it from the context and after that you will try to attach another instance with the same primary key to the same context instance.
If you really have to do this use this code instead:
public void Save(Street street)
{
string postalCode = street.PostalCode;
string streetCode = steet.StreetCode;
bool existingStreet = streetContext.Streets.Any(s =>
s.PostalCode == postalCode
&& s.StreetCode = steetCode);
if (existingStreet)
streetContext.Entry(street).State = System.Data.EntityState.Modified;
else
streetContext.Entry(street).State = System.Data.EntityState.Added;
streetContext.SaveChanges();
}
Anyway it is still not reliable solution in highly concurrent system because other thread can insert the same street between you check and subsequent insert.

Sequence contains no elements Linq-to-Sql

I'm having very strange problem in this simple linq query
return (from doc in db.umDocumentActions
where doc.sysDocument.ModuleID == modid
join roleaction in db.umRoleActions on doc.DocumentActionID equals roleaction.DocumentActionID
where roleaction.RoleID == roleID
select new { doc.DocumentID, roleaction.RoleID }).Count() > 0;
When this query is called it gives invalid operation exception telling me that sequence contains no elements. It happens when there is fair amount of traffic on the site. I am using following static method to get instance of datacontext.
public static EvoletDataContext Get()
{
var connection = ProfiledDbConnection.Get(new SqlConnection(ConfigurationManager.ConnectionStrings["cnstring"].ToString()));
return new EvoletDataContext(connection);
//return DataContextUtils.CreateDataContext<EvoletDataContext>(connection);
}
I'm afraid that this method is creating problem as static methods are not thread safe. Any views?
My best guess would be that sysDocument is actually a seperate table with a reference to DocumentID. This would normally mean that there would be a related collection of sysDocuments in the document class, but I'm guessing that you've changed the cardinality to "one to one" in the Linq to SQL designer.
When using a "one to one" cardinality, Linq to SQL uses the Single() method behind the scenes to get the sysDocument. This means that if there are no related sysDocuments you will get an invalidoperation exception.
You can fix this by wither changing the cardinality in you Linq model from "one to one" to "one to many" and use the SingleOrDefault() method to get the related sysDocument from the sysDocuments collection.
If that doesn't sound appealing you can look in the database to find which document doesn't have a related sysDocument and fix it manually.
UPDATE:
Instead of basing the query off the documentActions, try basing it off the sysDocument table instead. I've had to guess at what the table will be called so this might not compile, but hopefully you get the idea:
var query = from sysDocument in db.sysDocuments
where sysDocument.ModuleID == modid
let doc = sysDocument.umDocumentAction
join roleaction in db.umRoleActions on doc.DocumentActionID equals roleaction.DocumentActionID
where roleaction.RoleID == roleID
select new { doc.DocumentID, roleaction.RoleID };
//return true if there are any results (this is more efficient than Count() > 0)
return query.Any();

Categories

Resources