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!
Related
What i would like to do is, when a user does a search and the query doesn't return a result. I want to be able to log the string that was searched in another table.
What I'm trying to do seems like it should be straight forward... My first approach was to get the count of a PreprocessQuery.
Example of what i tried:
int count = 0;
partial void QueryName_PreprocessQuery(string ParamName, ref IQueryable<TableName> query)
{
if (ParamName != null)
{
query = query.Where(a => a.PropertyName.Contains(ParamName));
count = query.Count();
}
}
When i place a breakpoint and step through this method. count is always 0 and query is always empty. The odd thing is, this query still brings back the correct results. I've tried various different approaches and even started a new Lightswitch project with a single screen and a single query. Same results.
Another example of what i have tried:
partial void QueryName_PreprocessQuery(string ParamName, ref IQueryable<TableName> query)
{
if (ParamName != null)
{
count = query.Count(a => a.Serial_No.Contains(SerialNumber));
}
}
This method doesn't actually do anything except validate that query is empty and count is 0. And again... the correct results still show up on my screen.
So this leads me to believe that maybe i shouldn't be trying to get the count in the _PreProcessQuery Method?
I believe you're right. Try changing from QueryName_PreprocessQuery to QueryName_Executed instead.
QueryName_PreprocessQuery occurs when the query is being formed and prior to execution.
QueryName_Executed is called just after the query is executed and thus should give you the information you're trying to obtain.
You could try something like this:
partial void QueryName_Executed(QueryExecutedDescriptor queryDescriptor)
{
int resultCount = queryDescriptor.Results.Count();
}
I'm trying to accomplish 2 things with the below snippet of code (from ApplicationDataService.lsml.cs in the server project of my Lightswitch 2013 solution).
partial void Query1_PreprocessQuery(ref IQueryable<CandidateBasic> query)
{
query = from item in query where item.CreatedBy == this.Application.User.Name select item;
}
partial void CandidateBasics_Validate(CandidateBasic entity, EntitySetValidationResultsBuilder results)
{
var newcandidateCount = this.DataWorkspace.ApplicationData.Details.GetChanges().AddedEntities.OfType<CandidateBasic>().Count();
var databasecandidateCount = this.CandidateBasics.GetQuery().Execute().Count();
const int maxcandidateCount = 1;
if (newcandidateCount + databasecandidateCount > maxcandidateCount)
{
results.AddEntityError("Error: you are only allowed to have one candidate record");
}
}
Firstly, I want to make sure each user can only see things that he has made. This, together with a preprocess query on the table in question, works perfectly.
The next bit is designed to make sure that each user can only create one record in a certain table. Unfortunately, it seems to be looking at the whole table, and not the query I made that shows only the user's own records.
How can I get that second bit of code to limit only the user's own records, and not the global table?
You're not actually calling that query though are you? Your query is called Query1 based on the code provided yet you don't seem to be calling it. I'd do something like:
int count = DataWorkspace.ApplicationData.Query1().Count();
Currently, I am struggling with an issue regarding Entity Framework (LINQ to Entities). Most of the time when I try to execute entity.SaveChanges() everything works fine but at some points entity.SaveChanges() takes too much and timesouts. I searched a lot but was unable to find out the answer.
(According to companies policy, I cannot copy code somewhere else. So, I do not have the exact code but I will try to layout the basic structure. I hope it helps you to figure out the problem but if i doesn't then let me know.)
Task:
My task is to scan the whole network for some specific files. Match content of each file with the content of database and based on the matching either insert or update the database with the content of the file. I have around 3000 files on the network.
Problem:
public void PerformAction()
{
DbTransaction tran = null;
entity.Connection.Open(); //entity is a global variable declared like myDatabaseEntity entity = new myDatabaseEntity();
tran = entity.Connection.BeginTransaction();
foreach(string path in listOfPaths)
{
//returns 1 - Multiple matching in database OR
// 2 - One matching file in database OR
// 3 - No Matching found.
int returnValue = SearchDatabase();
if(returnValue == 1)
DoSomething(); //All inserts/updates work perfectly. Save changes also works correctly.
else if(returnValue == 2)
DoSomething(); //Again, everything ok. SaveChanges works perfectly here.
else
{
//This function uses some XML file to generate all the queries dynamically
//Forexample INSERT INTO TABLEA(1,2,3);
GenerateInsertQueriesFromXML();
ExecuteQueries();
SaveChanges(); <---- Problem here. Sometimes take too much time.
}
//Transaction commit/rollback code here
}
}
public bool ExecuteQueries()
{
int result = 0;
foreach(string query in listOfInsertQueries)
{
result = entity.ExecuteStoreCommand(query); //Execute the insert queries
if(result <=0)
return false;
}
entity.TestEntityA a = new entity.TestEntityA();
a.PropertyA = 123;
a.PropertyB = 345;
//I have around 25 properties here
entity.AddToTestEntityA(a);
return true;
}
Found the issue.
The main table where i was inserting all the data had a trigger on INSERT and DELETE.
So, whenever i inserted some new data in the main table, the trigger was firing in the backend and was taking all the time.
Entity framework is FAST and INNOCENT :D
I have a problem with some code that simply can’t work in LINQ, but it does work as a simple for..each. Any explanation and solution would be appreciated.
I have 3 classes, Users, User , UserPermissions composed as follows:
Class Users
ObservableCollection<User> GetList
Class User
Public int id {get;set;}
Public string UserName {get;set;}
Public UserPermissions Permissions {get;set;}
Class UserPermissions
Public Int ID {get;set;}
Public int ApplicationID {get;set;}
This works and returns the correct user:
Users users = new Users();
foreach (User u in users.GetList() )
{
if (u.UserName==username && u.Permissions.ApplicationID == applicationId)
{
usr = u;
break;
}
}
The linq below 'should' do the same thing, but it doesn’t. There are no errors returned or raised in the output window, and the musers variable simply doesn't exist after stepping over it. I have tried being more specific in my casts and using AsQueryable. I even tried let p=u.Permissions, using two from commands, but nothing seems to fix it.
My worry is that my other classes will suffer from this and cause issues later on as more complex queries are used.
var musers = from Users.User u in UsersList
where (u.UserName==userName)
&& (u.Permissions.ApplicationID == ApplicationId)
select u.ID;
One more bit of information the following errors too?
var t1 = UsersList.SelectMany( u => u.Permissions);
Error 1 The type arguments for method 'System.Linq.Enumerable.SelectMany(System.Collections.Generic.IEnumerable, System.Func>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
var usr = users.GetList()
.FirstOrDefault(
p => p.UserName == username
&& p.Permissions.ApplicationID == applicationId);
Should actually do it for you. FirstOrDefault can return null, if no user has been found...
Adam was spot on thank you Adam.
I have seen in the debugger that 'var found=from...' showed that found is created and contains a value at the point where the linq statement is run. However, as Adam correctly states that linq enumeration is deferred until the point you enumerate the query. The reason it all looked fine to me was that directly under the code that worked was a for loop which triggers the enumeration for THAT linq query. The others above it had no such enumeration so just looked as if they had failed silently!
I think the compiler was optimizing the function so that I could debug the linq query at the point it is in the code rather than where the enumeration occurs, nice trick but it completely wrong footed me! lol. It made me think that the query itself is evaluated to some degree even if the results aren't available until you use ToList(), Count() or other enumeration function.
for example in the code below only f3 will contain anything at all, the others are just, well, nothing because they are never enumerated!
var f1 = from .....;
var f2 = from ....;
var f3 = from ....;
do lots of other stuff, even call a function
int count = f3.Count();
What is interesting is that f1, f2 are nothing even after the line runs but f3 has a value immediately after and before the enumeration (Count) takes place, so i think compiler optimization/debug is playing a part here.
Hope this helps someone else :)
I have a MS SQL table that I don't have any control over and I need to write to. This table has a int primary key that isn't automatically incremented. I can't use stored procs and I would like to use Linq to SQL since it makes other processing very easy.
My current solution is to read the last value, increment it, try to use it, if I get a clash, increment it again and retry.
Something along these lines:
var newEntity = new Log()
{
ID = dc.Logs.Max(l => l.ID) + 1,
Note = "Test"
};
dc.Logs.InsertOnSubmit(newEntity);
const int maxRetries = 10;
int retries = 0;
bool success = false;
while (!success && retries < maxRetries)
{
try
{
dc.SubmitChanges();
success = true;
}
catch (SqlException)
{
retries++;
newEntity.ID = dc.Logs.Max(l => l.ID);
}
}
if (retries >= maxRetries)
{
throw new Exception("Bummer...");
}
Does anyone have a better solution?
EDIT: Thanks to Jon, I simplified the max ID calculation. I was still in SQL thinking mode.
That looks like an expensive way to get the maximum ID. Have you already tried
var maxId = dc.Logs.Max(s => s.ID);
? Maybe it doesn't work for some reason, but I really hope it does...
(Admittedly it's more than possible that SQL Server optimises this appropriately.)
Other than that, it looks okay (smelly, but necessarily so) to me - but I'm not an expert on the matter...
You didn't indicate whether your app is the only one inserting into the table. If it is, then I'd fetch the max value once right after the start of the app/webapp and use Interlocked.Increment on it every time you need next ID (or simple addition if possible race conditions can be ruled out).
You could put the entire operation in a transaction, using a TransactionScope class, like below:
using (TransactionScope scope = new TransactionScope()){
var maxId = dc.Logs.Max(s => s.ID);
var newEntity = new Log(){
ID = maxId,
Note = "Test"
};
dc.Logs.InsertOnSubmit(newEntity);
dc.SubmitChanges();
scope.Complete();
}
By putting both the retrieval of the maximum ID and the insertion of the new records within the same transaction, you should be able to pull off an insert without having to retry in your manner.
One problem you might face with this method will be transaction deadlocks, especially if the table is heavily used. Do test it out to see if you require additional error-handling.
P.S. I included Jon Skeet's code to get the max ID in my code, because I'm pretty sure it will work correctly. :)
Make the id field auto incrementing and let the server handle id generation.
Otherwise, you will run into the problem liggett78 said. Nothing prevents another thread from reading the same id in between the reading and submitting of max id for this thread.