Can WPF handle Guids from LINQ to SQL? - c#

I stumbled on a strange problem. In my WPF (.NET 4) window I have a simple combobox (DisplayMemberPath="Name" SelectedValuePath="Id"). When I load the window I set the combobox's ItemSource property to context.Currencies.ToList() using LINQ to SQL. The Currency table in SQL is simply [Id] [uniqueidentifier] NOT NULL PRIMARY KEY, [Name] char NOT NULL. In .NET this translates to Id = System.Guid, Name = System.String.
The problem I'm having is that the call to combobox.ItemsSource = context.Currencies.ToList(); throws a FormatException (Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)).
What I can't understand is why is this error thrown?
If I leave LINQ to SQL out of the picture, define a "test" class - public class Test { public Guid Id; public string Name; }, set the comobox.ItemsSource to a List<Test> which contains some entries, then everything works.
If I do this:
combobox.ItemsSource = context.Currencies.Select(c => new { c.Id.ToString(), c.Name }).ToList()
then everything works.
I tried setting the current thread's Culture and UICulture to "en-US" beforehand as well (just in case it's a culture problem, my default culture is "et-EE"), but that didn't help either.
I looked at the generated LINQ to SQL classes from the designer, but couldn't find any properties to alter which might have helped with this error.
Am I missing something here or is this really a bug in the .NET framework?´
PS! The Currency table contains valid entries (valid Guids).

As Thomas pointed out, I had a binding on SelectedValue and it turns out the binding also contained a FallbackValue = 1. After removing the fallback property everything works. The property was scrolled out of view, so indeed it was something I missed.

Related

How to display the string Enum values instead of the number value using SQL

I'm trying to display a list of all Deliveries with the status Dispatched. However, its only returning the number value of the status as opposed to the actual string value. I think this is because I have used Enum to store my status values?
I wish to display the word Dispatched instead of the number value that it represents in the Enum. 
I'm developing in ASP.Net MVC and I'm using the query builder in VS2013.
I'm not sure how to approach this, can anyone please suggest an easy to understand solution using SQL.
Let me know if any additional code is required, and thank you in advance!
Here's the Query I want but it doesn't work:
SELECT Delivery.[Status],
COUNT(Delivery.DeliveryID) AS Dispatched_Status
FROM Delivery
WHERE Delivery.[Status] = 'Dispatched'
GROUP BY Delivery.[Status];
Here's the Query that does work but returns a number value. I tried it this way because Enum stores the string value as a number:
SELECT Delivery.[Status],
COUNT(Delivery.DeliveryID) AS Dispatched_Status
FROM Delivery
WHERE Delivery.[Status] = '1'
GROUP BY Delivery.[Status];
P.S I'm aware that status is a reserved word - will be following the correct naming conventions in future.
Delivery Table Definion
It sounds like you just need to add a lookup table in you DB. Something like
CREATE TABLE [dbo].[StatusLookup](
[StatusID] [int] NOT NULL,
[StatusName] [varchar](64) NOT NULL,
[StatusDescription] [varchar](max),
)
INSERT INTO [dbo].[StatusLookup]([StatusID],[StatusName],[StatusDescription]
VALUES(1, 'Dispatched', 'A dispatched record')
...
Note you'll have to manually do this and make sure to populate it with values that match up with your enum.
Then your query would be
SELECT StatusLookup.[StatusName],
COUNT(Delivery.DeliveryID) AS Dispatched_Status
FROM Delivery
JOIN StatusLookup ON Delivery.Status = StatusLookup.StatusID
WHERE StatusLookup.[StatusName] = 'Dispatched'
GROUP BY StatusLookup.[StatusName];
Enums are stored as integers by default.
You can add a separate varchar or nvarchar field to your database table to hold the description of the enum, and populate it using something like the below:
string selectedEnumDescription = Enum.GetName(typeof(DeliveryStatusEnum), Delivery.Status)
The exact implementation depends on how you are saving your records, and what the actual properties and enum names are.
You can then just select the description column in your SQL query.
Either that or you could store the actual enum values and descriptions within a separate table and do a join.
You can store enum in database as a number, usually a small number - the exact type depends on your database. When you read it - you convert a number to enum and work in your code with the enum. When you need to display it, you can call a ToString() method on that enum, for example
public enum Foo
{
A,
B
}
public class Program
{
public static void Main()
{
Console.WriteLine(Foo.A.ToString()); // Prints A
}
}
See it working
You can also use description attribute and print that, see examples here and here

Limiting Characters or Lines - Asp MVC C#

I'm fairly new to asp mvc and I'm currently trying to limit the text on an item in my site, how can i limit the text for:
#Html.DisplayFor(model => model.Text)
I have tried just creating another item in the model like this:
public string Text {get; set; }
private string _limitText;
[StringLength(30)]
public string limitText
{
get
{
return (_limitText = this.Text)
}
set
{
_limitedText = Text;
}
}
However i get errors when updating database with the Nuget Console:
"String or binary data would be truncated. the statement has been terminated"
All help appreciated.
It seems you are using EF code first and trying to upgrade your DB using code first migrations.
Since you have already created table with VARCHAR(MAX) length (If you do not specify length on your property it will be considered as max length by EF), you will need to first delete all records from DB which has length more then 30 for limittext column. Else you wont be able to upgrade your version using code first migration.
If you do not want to loose your data then clone your table and then migrate your database and then copy your data back to original table.
You can clone table using following query
SELECT *
INTO CustomerClone
FROM Customer
Your chief problem here is that your new property, limitText has both a getter and setter and you're filling it with the full text from Text. If a property has a getter and setter, then Entity Framework persists it to the database. That alone is probably not ideal, since you're basically duplicating data, but let's say you wanted it to actually be persisted. It's value is set to the full string of Text, but it's going to then be persisted into a column that has a maxlength of 30 characters, hence the error. There's many ways to fix this problem. First, you can simply set the value to just the truncated string:
_limitText = this.Text.Substring(0, 30);
Then, it will save fine because the value will always be 30 characters of less. However, more likely than not, you simply shouldn't persist this field. That can be accomplished by either 1) removing the setter or 2) decorating the property with [NotMapped].
Also, you could simply not use this extra property at all. There's no need to use Html.DisplayFor here, so you could simply just do the following in your view instead:
#Model.Text.Substring(0, 30)
There are simple 2 ways how the issue can be solved:
First, you can use attribute maxlength to specify maximum length of the text that can be input. In this case you need to change DisplayFor to TextBoxFor and use maxlength attribute:
#Html.TextBoxFor(model => model.Text, new { maxlength=30 } )
Second, you can take first 30 symbols of the property by using Substring function. This is not very nice approach but it works.

DbContext.Database.SqlQuery returns some null objects

Using Entity Framework 6, the code below queries a table (really, a view) and returns some number of rows. The number of rows is correct, but many of the rows are null, i.e. some of the Licenses objects are null, as seen in the Visual Studio Watch window. I think what is happening is that if any column of a given row in the view contains a null, then the entire row is being set to null.
Any ideas on how to get all the correct data?
String query = "select * from dbo.v_Licenses where [Number] like '%ab%'";
System.Data.Entity.Infrastructure.DbRawSqlQuery<Licenses> dbRawSqlQuery = db.Database.SqlQuery<Licenses>(query);
Queryable<Licenses> licenses = dbRawSqlQuery.AsQueryable();
Be sure that License properties'name are identical with columns that are fetched from the select and property type are identical, too.
Like this:
Select CustomerId,Firstname from Customers
public class Customer
{
public int CustomerId{get;set;}
public string Firstname {get;set;}
}
and change System.Data.Entity.Infrastructure.DbRawSqlQuery to List
I used this approach a lot and it worked very nice for me.
Based on this question it seems like EF (at least at one point) returned null objects if the first column of the result set was null. So rather then selecting * you should explicitly name the columns and ensure that the PK (or some other column) is first.
In my case it did not work because I did NOT have the fields as properties. Once I converted the fields to properties it worked just fine.

populate combobox with enum value

On form load (edit action) I want to combobox be selected with appropriate value. These value is of type LangEnum.
I tried with comboBoxLanguage.SelectedValue = book.Language; but combo is always populated with default value (first from enum list).
update:
book.Language is declared as
public enum EnumLang { English = 1, German = 2, Other = 3 };
I tried both with
comboBoxLang.SelectedItem = (Book.EnumLang)book.Language;
and with
comboBoxLang.SelectedItem = book.Language;
and nothing works (default first value (English) is always set) and worth to mention is that on debug mode book.Language is set To German or Other but English is selected on combobox.
That looks right to me!
I am doing the same thing, are you sure that book.Language string is an EXACT match to one of the items in the list?
And is the list populated BEFORE you are trying to SelectedValue?
Better try:
comboBoxLanguage.SelectedItem = book.Language;
// or even
comboBoxLanguage.Text = book.Language.ToString(); //should work
You might want to set the ValueMember property to get or set the SelectedValue.
Works fine for me using SelectedItem:
comboBoxLanguage.SelectedItem = book.Language;
You didn't tell what kind of variable book is: is it of type LangEnum? Or is it a class with a Language property? (in this last case: what is the type of the Language property?)
If book is of type LangEnum you can use the SelectedItem property as others have said. (Check this SO question if you need more informations of the differences between the two combobox properties)
Otherwise you'll probably need a cast:
comboBoxLanguage.SelectedItem = (LangEnum)book.Language;
Moreover, if you are populating your combobox inside a WinForm event, you should also care about the order in which they are fired. Take a look at this SO question form more infos.

Linq-to-Sql SubmitChanges not updating fields ... why?

I posted this question yesterday evening, which has led me to discover a huge problem!
I have a decimal column in my database called Units, anytime I set the value of the column to a NON ZERO, and SubmitChanges the column updates with the new value. If I try to set the value of the column to ZERO, the SubmitChanges does not update the column.
data.Units = this.ReadProperty<decimal>(UnitsProperty);
data.UnitPrice = this.ReadProperty<decimal>(UnitPriceProperty);
data.Price = this.ReadProperty<decimal>(PriceProperty);
I've taken a look at the DataContext log and I can see that the field with the ZERO value is not included in the query. Even if I try to hard code the change Linq ignores it.
data.Units = 0;
data.UnitPrice = 0;
data.Price = 0;
Needless to say this is killing me! Any ideas why this happening?
Solution
I figured out my problem with the help of the SO community. My problem was being caused by the fact when I created my entity to attach, the default value of the column was set to zero, so when it tried to assign the value to zero ... LinqToSql says hey ... nothing changed, so I am not updating the value.
What I am doing now ... just to make it work is the following:
ctx.DataContext.InvoiceItems.Attach(data, true);
That seems to force all the values to write themselves to the database. This works for now.
I have tried to reproduce this with a the following code, but for me it works.
using (DataClasses1DataContext ctx = new DataClasses1DataContext())
{
var obj = ctx.DecimalColumnTables.First();
Debug.Assert(obj.B != 0);
obj.B = 0;
ctx.SubmitChanges();
}
So I think there must be something special in your domain that causes this. I suggest you to create a such simple repro with your domain model and see what happens.
LINQ to SQL ignores updates to the current value, so if the field was already zero, you may not see any updates.
Off: The OR/M you use is LINQ to SQL. LINQ is the name of the querying capability in .NET, but LINQ does not define nor implement any update logic. So the issue relates to LINQ to SQL, not LINQ.
Obvious question, but are you sure the column is mapped in the dbml / mapping file?
Also - is it a calculated column? (i.e. price => units * unitprice)
I figured out my problem with the help of the SO community. My problem was being caused by the fact when I created my entity to attach, the default value of the column was set to zero, so when it tried to assign the value to zero ... LinqToSql says hey ... nothing changed, so I am not updating the value.
What I am doing now ... just to make it work is the following:
ctx.DataContext.InvoiceItems.Attach(data, true);
That seems to force all the values to write themselves to the database. This works for now.
Some more information ... I figured out my problem ... it's more of a lack of understanding about LinqToSql ... where I am doing:
private void Child_Update(Invoice parent)
{
using (var ctx = Csla.Data.ContextManager
.GetManager(Database.ApplicationConnection, false))
{
var data = new Gimli.Data.InvoiceItem()
{
InvoiceItemId = ReadProperty(InvoiceItemIdProperty)
};
ctx.DataContext.InvoiceItems.Attach(data);
if (this.IsSelfDirty)
{
// Update properties
}
}
}
I thought this would load the original values ... what happens is that it creates a new object with default values ... empty values, like 0 for decimals, Guid.Empty for uniqueidentifiers and so on.
So when it updates the properties it sees the Units already as 0 and it sets it to zero. Well LinqToSql doesn't recognize this as a change so it doesn't up date the field. So what I have had to do is the following:
ctx.DataContext.InvoiceItems.Attach(data, true);
Now all the modifications are generated in the update statement whether there is really a change or not. This works ... seems a bit hackish!
The correct answer is as many pointed out to use the special overload of Attach which accepts a boolean parameter to consider it as modified, (make the mistake of using another overload and it simply won't work):
ctx.DataContext.InvoiceItems.Attach(data, true);
Note however that you still might need to have a "Version" column in the table of type "timestamp".
I had this problem and all the suggestions I'd seen didn't apply or work.
But I found I had made a very simple mistake!
When updating the property I was actually calling a custom Set method (because there were other things that needed to be changed in response to the main property in question).
After hours of head scratching I noticed that my Set method was updating the private member not the public property, i.e. this._Walking = value;
All I had to do was change this to this.Walking = value; and it all started to work!

Categories

Resources