Entity Framework options to map list of strings or list of int (List<string>) - entity-framework

I want to store an object that contains a List of primitives using EF.
public class MyObject {
public int Id {get;set;}
public virtual IList<int> Numbers {get;set;}
}
I know that EF cannot store this, but I'd like to know possible solutions to solve this problem.
The 2 Solutions I can think of are:
1.Create a Dummy object that has an Id and the Integervalue, e.g.
public class MyObject {
public int Id {get;set;}
public virtual IList<MyInt> Numbers {get;set;}
}
public class MyInt {
public int Id {get;set;}
public int Number {get;set;}
}
2.Store the list values as a blob, e.g.
public class MyObject {
public int Id {get;set;}
/// use NumbersValue to persist/load the list values
public string NumbersValue {get;set;}
[NotMapped]
public virtual IList<int> Numbers {
get {
return NumbersValue.split(',');
}
set {
NumbersValue = value.ToArray().Join(",");
}
}
}
The Problem with the 2. approach is, that I have to create a Custom IList implementation to keep track if someone modifies the returned collection.
Is there a better solution for this?

Although I do not like to answer my own question, but here is what solved my problem:
After I found this link about Complex Types I tried several implementations, and after some headache I ended up with this.
The List values get stored as a string on the table directly, so it's not required to perform several joins in order to get the list entries. Implementors only have to implement the conversation for each list entry to a persistable string (see the Code example).
Most of the code is handled in the Baseclass (PersistableScalarCollection). You only have to derive from it per datatype (int, string, etc) and implement the method to serialize/deserialize the value.
It's important to note, that you cannot use the the generic baseclass directly (when you remove the abstract). It seems that EF cannot work with that. You also have to make sure to annotate the derived class with the [ComplexType] attribute.
Also note that it seems not to be possible to implement a ComplexType for IList<T> because EF complains about the Indexer (therefore I went on with ICollection).
It's also important to note, that since everything is stored within one column, you cannot search for values in the Collection (at least on the database). In this case you may skip this implementation or denormalize the data for searching.
Example for a Collection of integers:
/// <summary>
/// ALlows persisting of a simple integer collection.
/// </summary>
[ComplexType]
public class PersistableIntCollection : PersistableScalarCollection<int> {
protected override int ConvertSingleValueToRuntime(string rawValue) {
return int.Parse(rawValue);
}
protected override string ConvertSingleValueToPersistable(int value) {
return value.ToString();
}
}
Usage example:
public class MyObject {
public int Id {get;set;}
public virtual PersistableIntCollection Numbers {get;set;}
}
This is the baseclass that handles the persistence aspect by storing the list entries within a string:
/// <summary>
/// Baseclass that allows persisting of scalar values as a collection (which is not supported by EF 4.3)
/// </summary>
/// <typeparam name="T">Type of the single collection entry that should be persisted.</typeparam>
[ComplexType]
public abstract class PersistableScalarCollection<T> : ICollection<T> {
// use a character that will not occur in the collection.
// this can be overriden using the given abstract methods (e.g. for list of strings).
const string DefaultValueSeperator = "|";
readonly string[] DefaultValueSeperators = new string[] { DefaultValueSeperator };
/// <summary>
/// The internal data container for the list data.
/// </summary>
private List<T> Data { get; set; }
public PersistableScalarCollection() {
Data = new List<T>();
}
/// <summary>
/// Implementors have to convert the given value raw value to the correct runtime-type.
/// </summary>
/// <param name="rawValue">the already seperated raw value from the database</param>
/// <returns></returns>
protected abstract T ConvertSingleValueToRuntime(string rawValue);
/// <summary>
/// Implementors should convert the given runtime value to a persistable form.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected abstract string ConvertSingleValueToPersistable(T value);
/// <summary>
/// Deriving classes can override the string that is used to seperate single values
/// </summary>
protected virtual string ValueSeperator {
get {
return DefaultValueSeperator;
}
}
/// <summary>
/// Deriving classes can override the string that is used to seperate single values
/// </summary>
protected virtual string[] ValueSeperators {
get {
return DefaultValueSeperators;
}
}
/// <summary>
/// DO NOT Modeify manually! This is only used to store/load the data.
/// </summary>
public string SerializedValue {
get {
var serializedValue = string.Join(ValueSeperator.ToString(),
Data.Select(x => ConvertSingleValueToPersistable(x))
.ToArray());
return serializedValue;
}
set {
Data.Clear();
if (string.IsNullOrEmpty(value)) {
return;
}
Data = new List<T>(value.Split(ValueSeperators, StringSplitOptions.None)
.Select(x => ConvertSingleValueToRuntime(x)));
}
}
#region ICollection<T> Members
public void Add(T item) {
Data.Add(item);
}
public void Clear() {
Data.Clear();
}
public bool Contains(T item) {
return Data.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex) {
Data.CopyTo(array, arrayIndex);
}
public int Count {
get { return Data.Count; }
}
public bool IsReadOnly {
get { return false; }
}
public bool Remove(T item) {
return Data.Remove(item);
}
#endregion
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator() {
return Data.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() {
return Data.GetEnumerator();
}
#endregion
}

I'm using EF Core and had a similar problem but solved it in a simpler way.
The idea is to store the list of integers as a comma separated string in the database. I do that by specifying a ValueConverter in my entity type builder.
public class MyObjectBuilder : IEntityTypeConfiguration<MyObject>
{
public void Configure(EntityTypeBuilder<MyObject> builder)
{
var intArrayValueConverter = new ValueConverter<int[], string>(
i => string.Join(",", i),
s => string.IsNullOrWhiteSpace(s) ? new int[0] : s.Split(new[] { ',' }).Select(v => int.Parse(v)).ToArray());
builder.Property(x => x.Numbers).HasConversion(intArrayValueConverter);
}
}
More information can be found here: https://entityframeworkcore.com/knowledge-base/37370476/how-to-persist-a-list-of-strings-with-entity-framework-core-

Bernhard's answer is brilliant. I just couldn't help but refine it a little. Here's my two cents:
[ComplexType]
public abstract class EFPrimitiveCollection<T> : IList<T>
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int Id { get; set; }
const string DefaultValueSeperator = "|";
readonly string[] DefaultValueSeperators = new string[] { DefaultValueSeperator };
[NotMapped]
private List<T> _data;
[NotMapped]
private string _value;
[NotMapped]
private bool _loaded;
protected virtual string ValueSeparator => DefaultValueSeperator;
protected virtual string[] ValueSeperators => DefaultValueSeperators;
[ShadowColumn, MaxLength]
protected virtual string Value // Change this to public if you prefer not to use the ShadowColumnAttribute
{
get => _value;
set
{
_data.Clear();
_value = value;
if (string.IsNullOrWhiteSpace(_value))
return;
_data = _value.Split(ValueSeperators, StringSplitOptions.None)
.Select(x => ConvertFromString(x)).ToList();
if (!_loaded) _loaded = true;
}
}
public EFPrimitiveCollection()
{
_data = new List<T>();
}
void UpdateValue()
{
_value = string.Join(ValueSeparator.ToString(),
_data.Select(x => ConvertToString(x))
.ToArray());
}
public abstract T ConvertFromString(string value);
public abstract string ConvertToString(T value);
#region IList Implementation
public int Count
{
get
{
EnsureData();
return _data.Count;
}
}
public T this[int index]
{
get
{
EnsureData();
return _data[index];
}
set
{
EnsureData();
_data[index] = value;
}
}
public bool IsReadOnly => false;
void EnsureData()
{
if (_loaded)
return;
if (string.IsNullOrWhiteSpace(_value))
return;
if (_data.Count > 0) return;
if (!_loaded) _loaded = true;
_data = _value.Split(ValueSeperators, StringSplitOptions.None)
.Select(x => ConvertFromString(x)).ToList();
}
public void Add(T item)
{
EnsureData();
_data.Add(item);
UpdateValue();
}
public bool Remove(T item)
{
EnsureData();
bool res = _data.Remove(item);
UpdateValue();
return res;
}
public void Clear()
{
_data.Clear();
UpdateValue();
}
public bool Contains(T item)
{
EnsureData();
return _data.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
EnsureData();
_data.CopyTo(array, arrayIndex);
}
public int IndexOf(T item)
{
EnsureData();
return _data.IndexOf(item);
}
public void Insert(int index, T item)
{
EnsureData();
_data.Insert(index, item);
UpdateValue();
}
public void RemoveAt(int index)
{
EnsureData();
_data.RemoveAt(index);
UpdateValue();
}
public void AddRange(IEnumerable<T> collection)
{
EnsureData();
_data.AddRange(collection);
UpdateValue();
}
public IEnumerator<T> GetEnumerator()
{
EnsureData();
return _data.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
EnsureData();
return _data.GetEnumerator();
}
#endregion
}
With that base class you can have as many derivations as you like:
[ComplexType]
public class EFIntCollection : EFPrimitiveCollection<int>
{
public override int ConvertFromString(string value) => int.Parse(value);
public override string ConvertToString(int value) => value.ToString();
}
[ComplexType]
public class EFInt64Collection : EFPrimitiveCollection<long>
{
public override long ConvertFromString(string value) => long.Parse(value);
public override string ConvertToString(long value) => value.ToString();
}
[ComplexType]
public class EFStringCollection : EFPrimitiveCollection<string>
{
string _separator;
protected override string ValueSeparator => _separator ?? base.ValueSeparator;
public override string ConvertFromString(string value) => value;
public override string ConvertToString(string value) => value;
public EFStringCollection()
{
}
public EFStringCollection(string separator)
{
_separator = separator;
}
}
EFPrimitiveCollection works just like a list, so you shouldn't have any issues using it like a normal List. Also the data is loaded on demand.
Here's an example:
if (store.AcceptedZipCodes == null)
store.AcceptedZipCodes = new EFStringCollection();
store.AcceptedZipCodes.Clear();
store.AcceptedZipCodes.AddRange(codes.Select(x => x.Code));
Shadow Column
This attribute is being used to abstract away the Value property. If you do not see the need to do this, simply remove it and make the Value property public.
More information can be found on the ShadowColumnAttribute in my answer here

The simple solution would be in your DataContext (a class that implements DbContext) add this override:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<MyObject>()
.Property(p => p.Numbers)
.HasConversion(
toDb => string.Join(",", toDb),
fromDb => fromDb.Split(',').Select(Int32.Parse).ToList() ?? new List<int>());
}

Related

Entity framework Generic query in Nongeneric Property

In Entity framework I have objectsets like
public partial class Building
{
public int BuildingID { get; set; }
public string BuildingName { get; set; }
}
public partial class Town
{
public int TownID { get; set; }
public string TownName { get; set; }
}
I want to create a generic query like
T.OrderBy(o=>o.Id).Skip(maxDispItem * (page - 1)).Take(maxDispItem).ToList();
T is generic class can be Building or Town but problem is BuildingId and TownId has different name.I don't want to change their name as Id and create interface IIdentity.
Maybe you could try something like this:
var query = (typeof(T) == typeof(Building) ?
context.Buildings.Select(b => new { Id = b.BuildingId, Name = b.BuildingName }) :
context.Towns.Select(t => new { Id = t.TownId, Name = b.TownName }))
.OrderBy(o => o.Id)...
Not tested but that's worth a test...
You can create generic method which find a field decorated with KeyAttribute, and then performs sorting by found key field. I have tested your model, works perfectly. Look at code snippet.
DbContext:
using System.Collections.Generic;
using System.Data.Entity;
namespace ConsoleApplication28.Entities
{
public class AppDbContext : DbContext
{
public AppDbContext()
{
Database.Connection.ConnectionString = #"Data Source=NOTEBOOK-PC;Initial Catalog=StackOverflowTest;Integrated Security=True";
Database.SetInitializer(new AppDbInitializer());
}
public DbSet<Town> Towns { get; set; }
public DbSet<Building> Buildings { get; set; }
}
public class AppDbInitializer : DropCreateDatabaseIfModelChanges<AppDbContext>
{
protected override void Seed(AppDbContext context)
{
context.Buildings.AddRange(new List<Building>
{
new Building {BuildingName = "Building1"},
new Building {BuildingName = "Building2"},
});
context.Towns.AddRange(new List<Town>
{
new Town {TownName = "Town1"},
new Town {TownName = "Town2"},
});
context.SaveChanges();
base.Seed(context);
}
}
}
Building
using System.ComponentModel.DataAnnotations;
namespace ConsoleApplication28.Entities
{
public class Building
{
[Key]
public int BuildingID { get; set; }
public string BuildingName { get; set; }
}
}
Town
using System.ComponentModel.DataAnnotations;
namespace ConsoleApplication28.Entities
{
public class Town
{
[Key]
public int TownID { get; set; }
public string TownName { get; set; }
}
}
Program
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using ConsoleApplication28.Entities;
using System.ComponentModel.DataAnnotations;
namespace ConsoleApplication28
{
class Program
{
static void Main(string[] args)
{
const int maxDispItem = 10;
const int page = 1;
var db = new AppDbContext();
var towns = db.Towns.OrderByKey().Skip(maxDispItem * (page - 1)).Take(maxDispItem).ToList();
var buildings = db.Buildings.OrderByKey().Skip(maxDispItem * (page - 1)).Take(maxDispItem).ToList();
}
}
public static class Extensions
{
/// <summary>
/// Sorts the elements of a sequence in ascending order according to a key specified using KeyAttribute
/// </summary>
public static IOrderedQueryable<T> OrderByKey<T>(this IQueryable<T> source, bool isAsc = true)
{
var type = typeof(T);
var keyProperty = type.GetProperties().Single(x => x.GetCustomAttributes(typeof(KeyAttribute)).Any());
return source.OrderBy(keyProperty.Name, isAsc);
}
#region COPIED FROM THERE http://stackoverflow.com/questions/41244/dynamic-linq-orderby-on-ienumerablet
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property, bool isAsc)
{
return isAsc ? source.OrderBy(property) : source.OrderByDescending(property);
}
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenByDescending");
}
static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
string[] props = property.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
object result = typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
#endregion
}
}

How to implement code first with existing database

I am using entityframework 5 with code first model and existing database.I am implementing repository pattern.I have a BaseEntity class like below:
public abstract partial class BaseEntity
{
/// <summary>
/// Gets or sets the entity identifier
/// </summary>
public virtual int Id { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as BaseEntity);
}
private static bool IsTransient(BaseEntity obj)
{
return obj != null && Equals(obj.Id, default(int));
}
private Type GetUnproxiedType()
{
return GetType();
}
public virtual bool Equals(BaseEntity other)
{
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
if (!IsTransient(this) &&
!IsTransient(other) &&
Equals(Id, other.Id))
{
var otherType = other.GetUnproxiedType();
var thisType = GetUnproxiedType();
return thisType.IsAssignableFrom(otherType) ||
otherType.IsAssignableFrom(thisType);
}
return false;
}
public override int GetHashCode()
{
if (Equals(Id, default(int)))
return base.GetHashCode();
return Id.GetHashCode();
}
public static bool operator ==(BaseEntity x, BaseEntity y)
{
return Equals(x, y);
}
public static bool operator !=(BaseEntity x, BaseEntity y)
{
return !(x == y);
}
protected virtual void SetParent(dynamic child)
{
}
protected virtual void SetParentToNull(dynamic child)
{
}
protected void ChildCollectionSetter<T>(ICollection<T> collection, ICollection<T> newCollection) where T : class
{
if (CommonHelper.OneToManyCollectionWrapperEnabled)
{
collection.Clear();
if (newCollection != null)
newCollection.ToList().ForEach(x => collection.Add(x));
}
else
{
collection = newCollection;
}
}
protected ICollection<T> ChildCollectionGetter<T>(ref ICollection<T> collection, ref ICollection<T> wrappedCollection) where T : class
{
return ChildCollectionGetter(ref collection, ref wrappedCollection, SetParent, SetParentToNull);
}
protected ICollection<T> ChildCollectionGetter<T>(ref ICollection<T> collection, ref ICollection<T> wrappedCollection, Action<dynamic> setParent, Action<dynamic> setParentToNull) where T : class
{
if (CommonHelper.OneToManyCollectionWrapperEnabled)
return wrappedCollection ?? (wrappedCollection = (collection ?? (collection = new List<T>())).SetupBeforeAndAfterActions(setParent, SetParentToNull));
return collection ?? (collection = new List<T>());
}
}
Now I have a table Customer which has CustomerId as primary key .How should I map such like fields with Id of BaseEntity.If I have a composite key how should I map this to the BaseEntity ID.
Please Help me.
Make sure you set the initialize for your DBContext to false:
context.Database.Initialize(false);
Here is a good article on database initializers for code first:
http://www.codeguru.com/csharp/article.php/c19999/Understanding-Database-Initializers-in-Entity-Framework-Code-First.htm

Json.net deserialize interface, concrete class uses a JsonConverter

My problem is that my JsonConverter doesn't seem to get invoked by the json.net de-serialization process when the converter is applied to an implementation of an interface, and the propertytype is the interface.
I use TypeNameHandling = TypeNameHandling.Objects to add $type to the json. I do so both on serialization and on de-serialization.
And when I have a property that is of an implementation of the interface the class' converter is invoked properly.
But when I have a property of interface type, the concrete class' converter is not invoked.
When I deserialize this class my JsonDataBagCreationConverter will be invoked by the RealTelephone but not by the Telephone because this is an interface.
Even though they are both serialized with the correct $type.
This results in RealTelephone having its .Data filled whereas Telephones .Data is null.
[JsonConverter(typeof(JsonDataBagCreationConverter<ContainerForITelephone>))]
public class ContainerForITelephone : IDataBag
{
private object _data;
private DataBagTypeEnum _dataBagTypeEnum;
public ITelephone Telephone { get; set; }
public Telephone RealTelephone { get; set; }
public object Data
{
get { return _data; }
set { _data = value; }
}
public DataBagTypeEnum DataBagType_Enum
{
get { return _dataBagTypeEnum; }
}
}
This jsonconverter is not invoked for the Telephone property. But it is for RealTelephone.
public class JsonDataBagCreationConverter<T> : JsonConverter where T : IDataBag, new()
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.Null)
{
var jsonObject = JObject.Load(reader);
var target = Create(objectType, jsonObject);
serializer.Populate(jsonObject.CreateReader(), target);
((IDataBag)target).Data = jsonObject.ToString();
return target;
}
return null;
}
}
[JsonConverter(typeof(JsonDataBagCreationConverter<Telephone>))]
public class Telephone : ITelephone
{
public string Name { get; set; }
public string AreaCode { get; set; }
public string Number { get; set; }
public SubPhone SubPhone { get; set; }
public object Data { get; set; }
public DataBagTypeEnum DataBagType_Enum { get; set; }
}
I look forward to hearing from you, thanks
Jan
SOLVED:
public class JsonDataBagCreationConverter<T> : JsonConverter where T:IDataBag
{
//, new() prevented us from using interfaces. Activator.CreateInstance did the trick in Create
//Used when the object decorated with [JsonConverter(typeof(JsonDataBagCreationConverter<xxxx>))] is de-serialized
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
if (objectType.IsInterface)
{
// Interfaces cannot be instantiated but must be converted to their "real" implemented type
// Because we serialize with settings.TypeNameHandling = TypeNameHandling.Objects;
// A $type property is added to the json by the deserializer.
string type = jsonObject["$type"].ToString();
var typesAsArray = type.Split(',');
var wrappedTarget = Activator.CreateInstance(typesAsArray[1], typesAsArray[0]);
var realTarget = wrappedTarget.Unwrap() as IDataBag;
serializer.Populate(jsonObject.CreateReader(), realTarget); // Will call this function recursively for any objects that have JsonDataBagCreationConverter as attribute
((IDataBag)realTarget).Data = jsonObject.ToString(); // This is where custom data is stored in databag
return realTarget;
}
// Non interface
var target = Create(objectType, jsonObject);
serializer.Populate(jsonObject.CreateReader(), target); // Will call this function recursively for any objects that have JsonDataBagCreationConverter as attribute
((IDataBag)target).Data = jsonObject.ToString(); // This is where custom data is stored in databag
return target;
}
public override bool CanRead
{
get
{
return true;
}
}
public override bool CanWrite
{
get
{
return false;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new Exception("WriteJson not implemented");
}
protected IDataBag Create(Type objectType, JObject jsonObject)
{
var aa = Activator.CreateInstance(objectType);
return aa as IDataBag;
// return new T(); // this demands ,new() on the class and then it will not work with interfaces
}
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
}

How to access to the object array

I have an auto generated class from an xml like the following:
public partial class XmlClass {
private decimal num1;
private ClassA[] classField;
/// <remarks/>
public decimal num1 {
get;
set;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("classA")]
public ClassA[] classA {
get{...};
set{...};
}
}
public partial class ClassA {
private object[] itemsField;
private string typeField;
[System.Xml.Serialization.XmlElementAttribute("commands", typeof(classACommands))]
[System.Xml.Serialization.XmlElementAttribute("minVersion", typeof(string))]
public object[] Items {
get {
return this.itemsField;
}
set {
this.itemsField = value;
}
}
[System.Xml.Serialization.XmlAttributeAttribute()]
public string type {
get {
return this.typeField;
}
set {
this.typeField = value;
}
}
}
ClassA has the string and commands class as Objects in the Object[]. I can see everything is deserialized perfectly and get them by: (commands)myXmlClass.classA.ElementAt(i).Items[3], where i is from the index of the ClassA array. But how can I get or set them without using '3'? It might be different in different ClassA elements.
Not sure I totally understand your question, but you can use a foreach loop:
foreach(ClassA a in myXmlClass.classA) {
Console.WriteLine(a.num1.ToString());
}

Unable to create a constant value of type. Only use primitive types

I've read some other posts but they have not helped.
CarPart is an EF4 gened class
[EdmEntityTypeAttribute(NamespaceName="xxxx.Data.Domain.Model", Name="CarPart")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class CarPart : EntityObject
{
#region Factory Method
/// summary>
/// Create a new CarPart object.
/// </summary>
/// <param name="carPartId">Initial value of the CarPartId property.</param>
/// <param name="name">Initial value of the Name property.</param>
/// <param name="carPartTypeId">Initial value of the CarPartId property.</param>
public static CarPart CreateCarPart(global::System.Int32 carPartId, global::System.String name, global::System.Int32 carPartId)
{
CarPart carPart = new CarPart();
carPart.CarPartId = carPartId;
carPart.Name = name;
carPart.CarPartTypeId = carPartTypeId;
return carPart;
}
#endregion
#region Primitive Properties
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Int32 CarPartId
{
get
{
return _CarPartId;
}
set
{
if (_CarPartId != value)
{
OnCarPartIdChanging(value);
ReportPropertyChanging("CarPartId");
_CarPartId = StructuralObject.SetValidValue(value);
ReportPropertyChanged("CarPartIdId");
OnCarPartIdChanged();
}
}
}
private global::System.Int32 _CarPartId;
partial void OnCarPartIdChanging(global::System.Int32 value);
partial void OnCarPartIdChanged();
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.String Name
{
get
{
return _Name;
}
set
{
OnNameChanging(value);
ReportPropertyChanging("Name");
_Name = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("Name");
OnNameChanged();
}
}
private global::System.String _Name;
partial void OnNameChanging(global::System.String value);
partial void OnNameChanged();
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Int32 CarPartTypeId
{
get
{
return _CarPartTypeId;
}
set
{
OnCarPartTypeIdChanging(value);
ReportPropertyChanging("CarPartTypeId");
_CarPartTypeId = StructuralObject.SetValidValue(value);
ReportPropertyChanged("CarPartTypeId");
OnCarPartTypeIdChanged();
}
}
private global::System.Int32 _CarPartTypeId;
partial void OnCarPartTypeIdChanging(global::System.Int32 value);
partial void OnCarPartTypeIdChanged();
#endregion
#region Navigation Properties
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[XmlIgnoreAttribute()]
[SoapIgnoreAttribute()]
[DataMemberAttribute()]
[EdmRelationshipNavigationPropertyAttribute("xxxx.Data.Domain.Model", "FK_CarPartId", "Part")]
public EntityCollection<Part> Parts
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<Part>("xxxxx.Data.Domain.Model.FK_CarPartId", "Part");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<Part>("xxxx.Data.Domain.Model.FK_CarPartId", "Part", value);
}
}
}
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[XmlIgnoreAttribute()]
[SoapIgnoreAttribute()]
[DataMemberAttribute()]
[EdmRelationshipNavigationPropertyAttribute("xxxxx.Data.Domain.Model", "FK_CarPartTypeId", "CarPartType")]
public CarPartType CarPartType
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<CarPartType>("xxxx.Data.Domain.Model.FK_CarPartTypeId", "CarPartType").Value;
}
set
{
((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<CarPartType>("xxxxx.Data.Domain.Model.FK_CarPartTypeId", "CarPartType").Value = value;
}
}
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[BrowsableAttribute(false)]
[DataMemberAttribute()]
public EntityReference<CarPartType> CarPartTypeReference
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<CarPartType>("xxxx.Data.Domain.Model.FK_CarPartTypeId", "CarPartType");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedReference<CarPartType>("xxxxxx.Data.Domain.Model.FK_CarPartTypeId", "CarPartType", value);
}
}
}
#endregion
}
So here's my join code:
List<Parts> parts = _context.Parts.Where(p => p.PartId == partId).ToList();
List<CarParts> parts = _context.CarParts
.Join(parts, cp => cp.PartId, p => p.PartId, (cp, p) => cp).ToList();
Error: Unable to create a constant value of type 'Model.CarParts'. Only
primitive types ('such as Int32, String, and Guid') are supported in
this context.
Tried to look at it but can't get past this. I'm a bit new to LINQ-To-SQL..done a fair amount but not a ton (mostly done LINQ to Objects) so new to joins with this.
if i'm understanding right you want to do something like this:
internal class Db
{
public Db()
{
var parts = new List<Part>
{
new Part() {PartId = 1, PartName = "Part 1"},
new Part() {PartId = 2, PartName = "Part 2"},
};
Parts = parts.AsQueryable();
var carParts = new List<CarPart>
{
new CarPart() {CarPartId = 1, CarPartName = "Car Part 1.1", PartId = 1},
new CarPart() {CarPartId = 1, CarPartName = "Car Part 1.2", PartId = 1},
new CarPart() {CarPartId = 1, CarPartName = "Car Part 2.1", PartId = 2},
};
CarParts = carParts.AsQueryable();
}
public IQueryable<Part> Parts { get; set; }
public IQueryable<CarPart> CarParts { get; set; }
}
internal class CarPart
{
public int CarPartId { get; set; }
public string CarPartName { get; set; }
public int PartId { get; set; }
}
internal class Part
{
public int PartId { get; set; }
public string PartName { get; set; }
}
static void Main(string[] args)
{
Db db = new Db();
var result = from carPart in db.CarParts
join part in db.Parts on carPart.PartId equals part.PartId
select new {Part = part, CarPart = carPart};
var lambdaResult = db.CarParts.Join(db.Parts, part => part.PartId, caPart => caPart.PartId,
(carPart, part) => new {CarPart = carPart, Part = part});
foreach (var item in result)
{
Console.WriteLine(item.Part.PartName);
Console.WriteLine(item.CarPart.CarPartName);
}
Console.WriteLine("------------");
foreach (var item in lambdaResult)
{
Console.WriteLine(item.Part.PartName);
Console.WriteLine(item.CarPart.CarPartName);
}
}
the result is a new anonymous object with the joined objects as content.
it would print this to the console:
Part 1
Car Part 1.1
Part 1
Car Part 1.2
Part 2
Car Part 2.1
-------
Part 1
Car Part 1.1
Part 1
Car Part 1.2
Part 2
Car Part 2.1