EF Code-First - Storing IEnumerable Of Enum - entity-framework-4.1

I'm using EF June CTP which has simple enum support. I have a MVC 3 view which has checkboxes that when submitted are retrieved as an array of some enum.
public enum CheckBox : byte
{
VALUE_1 = 1,
VALUE_2 = 2,
...
}
I need to take that colelction of enums ((IEnumerable)) and store that via code-first approach. Right now, that property is being ignored altogether.
I'm not sure if it would ultimately, when works, would create a separate one-to-many table or store all the values in one column but any ideas would be great. Just need to store it. Once it stores it, it would be ultimately be awesome if it didn't create a separate one-to-many table and somehow store it within the same table as some delimited string but I'm reaching here probably.
Thank you.
UPDATE 1
Tried adding [Flags] to the enum to no avail. It gets completely ignored during table generation. My Model property is:
public IEnumerable<CheckBox> Values { get; set; }

You should use a [Flags] enum instead of a collection:
[Flags]
public enum Checboxes {
SomeValue = 1,
OtherValue = 2,
ThirdValue = 4
}
public Checkboxes Boxes { get; set; }
Boxes = Checkboxes.SomeValue | Checkboxes.OtherValue;

Related

EF Core 7 can't deserialize dynamic-members in JSON column

I am trying to map my Name column to a dynamic object. This is how the raw JSON data looks (note that this is SQL-morphed from our old relational data and I am not able to generate or interact with this column via EF Core):
{ "en": "Water", "fa": "آب", "ja": "水", ... }
Just to note, available languages are stored in a separate table and thus are dynamically defined.
Through T-SQL I can perfectly interact with these objects eg
SELECT *
FROM [MyObjects]
WHERE JSON_VALUE(Name, '$.' + #languageCode) = #searchQuery
But it seems EF Core doesn't want to even deserialize these objects as whole, let alone query them.
What I get in a simple GetAll query is an empty Name. Other columns are not affected though.
I have tried so far
Using an empty class with a [JsonExtensionData] dictionary inside
Using a : DynamicObject inheritance and implementing GetDynamicMembers, TryGetMember, TrySetMember, TryCreateInstance
Directly mapping to a string dictionary.
Combining 1 & 2 and adding an indexer operator on top.
All yield the same results: an empty Name.
I have other options like going back to a junction table relational which I have many issues with, hardcoding languages which is not really intuitive and might cause problems in the future, using HasJsonConversion which basically destroys the performance on any search action... so I'm basically stuck here with this.
I think currently it's not fully supported:
You can not use dynamic operations on an expression tree like a Select statement because it needs to be translated.
JsonValue and JsonQuery requires a path to be resolved.
If you specify OwnsOne(entity = >entity.owned, owned => owned.ToJson()) and the Json could not be parsed you will get an error.
I suggest this workaround while the EF team improves the functionality.
Create a static class with static methods to be used as decoys in the expression tree. This will be mapped to the server built-in functions.
public static class DBF
{
public static string JsonValue(this string column, [NotParameterized] string path)
=> throw new NotSupportedException();
public static string JsonQuery(this string column, [NotParameterized] string path) => throw new NotSupportedException();
}
Include the database functions on your OnModelCreating method.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDbFunction(
typeof(DBF).GetMethod(nameof(DBF.JsonValue))!
).HasName("JSON_VALUE").IsBuiltIn();
modelBuilder.HasDbFunction(
typeof(DBF).GetMethod(nameof(DBF.JsonQuery))!
).HasName("JSON_QUERY").IsBuiltIn();
/// ...
modelBuilder.Entity(entity => {
//treat entity as text
entity.Property(x => x.Metadata)
.HasColumnType("varchar")
.HasMaxLength(8000);
});
}
Call them dynamically with LINQ.
var a = await _context.FileInformation
.AsNoTracking()
.Where(x => x.Metadata!.JsonValue("$.Property1") == "some value")
.Select(x => x.Metadata!.JsonValue("$.Property2"))
.ToListAsync();
You can add casts or even build anonymous types with this method.
My solution was I added a new class which has KEY and VALUE , which will represent the dictionary i needed :
public class DictionaryObject
{
public string Key { set; get; }
public string Value { set; get; }
}
and instead of having this line in the JSON class :
public Dictionary<string, string> Name { get; set; }
I changed to :
public List<DictionaryObject> Name { get; set; }
Hope it helps.

Save DropDownListFor multiple selected values

How can I save data for a multiple DropDownListFor? I have this code but the selected items aren't reaching the controller.
My code view:
#Html.DropDownListFor(model => model.Ingredients, new SelectList(Model.ListIngredients, "Id", "Description"), "Select the ingredients", new { multiple = "multiple", })
My model:
public ICollection<Ingredient> Ingredients { get; set; }
public ICollection<Ingredient> ListIngredients{ get; set; }
Ingredient.cs:
public string Description { get; set; }
public int Id { get; set; }
I have to change the name and id of my helper for data to be saved?
You are trying to bind the selected values into a collection of Ingredients.
However if you take a look at the posted values, they will look something like this:
...Ingredients=1&Ingredients=2...
That means the model binder will not know how to convert values like 1,2 (which are the ingredient Ids) into instances of the ingredient class.
You need to change the property of your view model where you will get the ingredients selected by the user, so instead of a collection of Ingredients:
public ICollection<Ingredient> Ingredients { get; set; }
you have a collection of Ids (which is the property you are using as the value of the multi-select), for example:
public int[] SelectedIngredients { get; set; }
(I have renamed the property but that is not strictly needed, I have done it just for clarity)
As a suggestion, I would remove the default option value from that multi-select (The "Select the ingredients" option) and use a label or tooltip. Otherwise it may look like any other value from the list and you will have to deal with the scenario where the user selects that option, which is not a valid ingredient.
Even better, I would also use the #Model.ListBoxFor helper as that will let everybody know they are dealing with a multi-select. Otherwise (with the drop down helper) you may not realize it is a multi-select unless you look at the attributes. Something like:
#Html.ListBoxFor(m=>m.SelectedIngredients, new SelectList(Model.ListIngredients, "Id", "Description"))
Of course, these are just suggestions. You can happily Ignore them if they don't apply to your requirements!
As a final comment, it is possible to convert the posted values into Ingredient objects as you initially had in your view model. That would require you to write your own model binder and configure it for the Ingredient type. However you will only receive the id in the posted values, so you would have to retrieve the other properties (from the database probably) or you will have a collection of Ingredient objects where only the Id is populated. I would not go down this route unless you really need to.

How to do property type conversion in EF 4.1 Code First

I want to create EF 4.1 Code first models from an existing SQL database schema and I am wondering if it is possible to do some type conversion of property data.
For example, I have an existing table "Foo" having a field like this:
isTrue char(1) 'valid values are "Y" or "N"
In my EF 4.1 Code First model, I want to convert this field into a boolean type like:
public class Foo
{
public bool isTrue { get; set; }
}
Is this possible in EF 4.1 Code First by extending DBContext or adding extra code in the model or EntityTypeConfiguration<> sub-class? If yes, can somebody point me to a link or some documentation on how to do it? Refactoring the database fields is not possible at this time.
It is possible to use a non-mapped field that is public which uses an internal field and then you can save and retrieve with code first and do the mapping here. This would needs to be done for each field that need to be converted or, of course, simplied with a helper method
internal string YesNo { get; set; }
private bool _bYesNo;
[NotMapped]
public bool bYesNo
{
get{return (YesNo == "Y") ? true : false;}
set{_bYesNo = value;YesNo = (bYesNo) ? "Y" : "N";}
}
It's not possible to have EF do the conversion.
One possible workaround is making EF ignore the bool property, and using a wrapper property that converts true to "Y" and false to "N".
If you usually need this kind of flexibility, I suggest you look into more mature frameworks.
NHibernate, for example, supports this requirement out of the box, by specifying YesNo as the mapping type.
I had the same problem and the following solution worked out great for me!!
Entity Framework and Oracle Boolean
In my code a table field just looks like a normal boolean, but in the database it is a char(1): The EF model will convert it into "Y" or "N" and saves it to the database.
I also do the same trick for Guid into char(36).

EF 4.1 Code First doesn't create column for List<string>

I have been playing around quite a lot with EF4 Code First and I do love it. However, I cannot seem to sort this easy one out.
When trying to create something like this, no columns are created in my database:
public IList<String> Recievers { get; set; }
public List<String> RecieversTest { get; set; }
public virtual List<String> RecieversAnotherTest { get; set; }
public virtual ICollection<Int32> RecieversAnotherTest { get; set; }
Ive tried Annotations to map it to a different column name, I've tried IEnumerable and all sorts of other collections, but it refuses to create a column for it.
After an hour on google I found one that claims she has done it, but I'm starting to doubt that. Should it even be possible?
I can't really see why it just doesn't create a column and use JSON or CSV.
It can't be that rare, can it? In my case i just want to store a list of emails.
What am I missing? The project creates all other types without problems, and I've inspected the database to see how other properties I add to test with gets created, while these gets ignored.
So the problem must lie in some setting I'm missing or some configuration....
EF 4.1 RTW on an SQL Server 2008 db.
I have bad news for you. EF doesn't do anything like that. If you want any serialization and deserialization you must do it yourselves = you must expose and map property with serialized value:
private IList<String> _receivers;
// This will be skipped
public IList<String> Receivers
{
get
{
return _receivers;
}
set
{
_receivers = value;
}
}
// This will be mapped
public string ReceiversSer
{
get
{
return String.Join(";", _receivers);
}
set
{
_receivers = value.Split(';').ToList();
}
}
Now ReceiversSer will be mapped to a column in the database.
You can't have a column based on a collection/list of something. A column is a singular item such as public string Receiver.
If you are expecting EF CF to take your IList or List and make several Columns out of it you are correct in that it won't.
In EF CF you create lists in your Entity to represent a relationship to another table. An Order may have many Items in it. You would in this case have an Order class with a list to an OrderItem object.
You would then have an OrderItem class to describe the OrderItem table. This would then essentially represent the 1 to many relationship of Order and OrderItems.

Linq - How to put common fields in a base class

I am trying to find a way so that I can push some common functionality into a base class for my Linq to SQL processing. I have two fields (ID and InsertUpdateOperID) that are common to most but not all of my tables. In my first go around I created a class called BaseEntity that had these fields. Unfortunately all I accomplished was hiding from the values in the .designer.cs file. I found an example of how to accomplish what I wanted In order to get around this (http://www.joe-stevens.com/2009/07/01/linq-to-sql-set-inheritance-modifiers-with-sqlmetal/). As per this article, I modifed the DBML file so that I could add the override modifier to the ID and InsertUpdateOperID properties on the tables that contained these two fields.
The net result of this was that the .designer.cs file added the override qualifier where I wanted it. This enabled me to create my BaseEntity class. Where I defined the ID field and the InsertUpdateOperID field as:
public virtual int ID { get; set; }
public virtual int InsertUpdateOperID { get; set; }
Doing this seemed to work fine.
The problem for me is that I hate the idea of modifying generated code. Can anyone suggest a way for me to put common fields and methods that act on those common fields in a base class so that I could accomplish what I want without modifying the generated .dbml?
Thanks
I'm facing the same problem today (wow, it's 1.5 years after your post), and struggled out a solution, so far it's good for me.
in the base class:
public virtual int __ID
{
get
{
PropertyInfo pi = this.GetType().GetProperty("ID");
int id = (int)pi.GetValue(this, new object[] {});
return id;
}
set
{
PropertyInfo pi = this.GetType().GetProperty("ID");
pi.SetValue(this, value, new object[] { });
}
}
This looks quite voilent, but it works!
Notice the __ before ID, because there is alread ID and _ID in the auto generated codes. As it's totally a new third way to access ID, no "override" is needed.
And if you need, you can use the __ID in or via your base class.