How to seed this entity model framework for complex relationship - c#

I have the following scenario. We need to be able to fill forms for some tables, examples Companies (Empresa in Spanish), however we want the administrator to be able to extend the entity itself with additional fields.
I designed the following classes, and I need to seed at least one row, however its unclear to me how to seed one row of type CampoAdicional
Entity class:
public abstract class Entidad
{
[Key]
public int Id { get; set; }
}
Company Class (Empresas)
public class Empresa : Entidad
{
public string Nombre { get; set; }
public string NIT { get; set; }
public string NombreRepresentanteLegal { get; set; }
public string TelefonoRepresentanteLegal { get; set; }
public string NombreContacto { get; set; }
public string TelefonoContacto { get; set; }
public virtual ICollection<CampoAdicional> CamposAdicionales { get; set; }
}
And the Additional Fields (Campo Adicional)
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public virtual Tiposcampo TipoCampo { get; set; }
public virtual Entidad Entidad { get; set; }
}
However I dont know how to seed this class or table, because entity should be of subtype Company
Obviously the typeof doesnt compile
context.CampoAdicionals.Add(new CampoAdicional() { Entidad = typeof(Empresa), Id = 1, NombreCampo = "TwitterHandle", TipoCampo = Tiposcampo.TextoUnaLinea });
Update 1: Please note that the additional fields are for the entire entity company not for each company.

Unfortunately, I don't think you'll be able to use EF to automatically create that kind of relationship. You might be able to do something similar with special getters and such:
public class Entidad
{
// stuff...
public IEnumerable<CampoAdicional> CamposAdicionales
{
get { return CampoAdicional.GetAll(this); }
}
}
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public virtual Tiposcampo TipoCampo { get; set; }
protected string EntidadType { get; set; }
// You will need some mapping between Type and the EntidadType string
// that will be stored in the database.
// Maybe Type.FullName and Type.GetType(string)?
protected Type MapEntidadTypeToType();
protected string MapTypeToEntidadType(Type t);
[NotMapped]
public Type
{
get { return MapEntidadTypeToType(); }
// maybe also check that Entidad.IsAssignableFrom(value) == true
set { EntidadType = MapTypeToEntidadType(value); }
}
public static IEnumerable<CampoAdicional> GetAll(Entidad ent)
{
return context.CampoAdicionals
.Where(a => a.EntidadType == MapTypeToEntidadType(ent.GetType()));
}
}

Related

JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than

In my web API when I run project to get data from the database got this error
.net core 3.1
JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32.
These are my codes:
my Model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }
public int ProductCategoryId { get; set; }
[JsonIgnore]
public virtual ProductCategory ProductCategory { get; set; }
}
my productCategory class is:
public class ProductCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string CatText { get; set; }
public string ImagePath { get; set; }
public int Priority { get; set; }
public int Viewd { get; set; }
public string Description { get; set; }
public bool Active { get; set; }
public DateTime CreateDate { get; set; }
public DateTime ModifyDate { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
my repo is
public async Task<IList<Product>> GetAllProductAsync()
{
return await _context.Products.Include(p => p.ProductCategory).ToListAsync();
}
my interface
public interface IProductRepository
{
...
Task<IList<Product>> GetAllProductAsync();
...
}
and this is my controller in api project
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly IProductRepository _productRepository;
public ProductsController(IProductRepository productRepository)
{
_productRepository = productRepository;
}
[HttpGet]
public ActionResult Get()
{
return Ok(_productRepository.GetAllProduct());
}
}
When I run API project and put this URL: https://localhost:44397/api/products
I got that error,
I can't resolve it
this is happening because your data have a reference loop.
e.g
// this example creates a reference loop
var p = new Product()
{
ProductCategory = new ProductCategory()
{ products = new List<Product>() }
};
p.ProductCategory.products.Add(p); // <- this create the loop
var x = JsonSerializer.Serialize(p); // A possible object cycle was detected ...
You can not handle the reference loop situation in the new System.Text.Json yet (netcore 3.1.1) unless you completely ignore a reference and its not a good idea always. (using [JsonIgnore] attribute)
but you have two options to fix this.
you can use Newtonsoft.Json in your project instead of System.Text.Json (i linked an article for you)
Download the System.Text.Json preview package version 5.0.0-alpha.1.20071.1 from dotnet5 gallery (through Visual Studio's NuGet client):
option 1 usage:
services.AddMvc()
.AddNewtonsoftJson(
options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
// if you not using .AddMvc use these methods instead
//services.AddControllers().AddNewtonsoftJson(...);
//services.AddControllersWithViews().AddNewtonsoftJson(...);
//services.AddRazorPages().AddNewtonsoftJson(...);
option 2 usage:
// for manual serializer
var options = new JsonSerializerOptions
{
ReferenceHandling = ReferenceHandling.Preserve
};
string json = JsonSerializer.Serialize(objectWithLoops, options);
// -----------------------------------------
// for asp.net core 3.1 (globaly)
services.AddMvc()
.AddJsonOptions(o => {
o.JsonSerializerOptions
.ReferenceHandling = ReferenceHandling.Preserve
});
these serializers have ReferenceLoopHandling feature.
Edit : ReferenceHandling changed to ReferenceHandler in DotNet 5
but if you decide to just ignore one reference use [JsonIgnore] on one of these properties. but it causes null result on your API response for that field even when you don't have a reference loop.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }
public int ProductCategoryId { get; set; }
// [JsonIgnore] HERE or
public virtual ProductCategory ProductCategory { get; set; }
}
public class ProductCategory
{
public int Id { get; set; }
// [JsonIgnore] or HERE
public ICollection<Product> products {get;set;}
}
.NET 5 Web API
public static void ConfigureServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddControllers()
.AddJsonOptions(o => o.JsonSerializerOptions
.ReferenceHandler = ReferenceHandler.Preserve);
}
I have the same issue, my fix was to add async and await keyword since I am calling an async method on my business logic.
Here is my original code:
[HttpGet]
public IActionResult Get()
{
//This is async method and I am not using await and async feature .NET which triggers the error
var results = _repository.GetAllDataAsync();
return Ok(results);
}
To this one:
HttpGet]
public async Task<IActionResult> Get()
{
var results = await _repository.GetAllDataAsync();
return Ok(results);
}
In .Net 6, you can use System.Text.Json to initialize a startup action with AddControllersWithViews like this in Program.cs,
using System.Text.Json.Serialization;
builder.Services.AddControllersWithViews()
.AddJsonOptions(x => x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
also you can use AddMvc like this,
builder.Services.AddMvc()
.AddJsonOptions(x => x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
but quote from Ryan
asp.net core 3.0+ template use these new
methodsAddControllersWithViews,AddRazorPages,AddControllers instead of
AddMvc.
I will recommend to use the first solution.
Ensure you have [JsonIgnore] on the correct fields to avoid a circular reference.
In this case you will need
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }
[JsonIgnore]
public virtual ProductCategory ProductCategory { get; set; }
}
You probably don't need the ProductCategoryId field (depends if you are using EF and code first to define your DB)
Edit - In answer to noruk
There is often confusion in connected objects and navigation properties. You can get the data you want in JSON but also define the EF structures to get the correct DB structure (foreign keys, indexes, etc).
Take this simple example. A Product (for example a T-Shirt) has many sizes or SKUs (e.g. Small, Large, etc)
public class Product
{
[Key]
[MaxLength(50)]
public string Style { get; set; }
[MaxLength(255)]
public string Description { get; set; }
public List<Sku> Skus { get; set; }
}
public class Sku
{
[Key]
[MaxLength(50)]
public string Sku { get; set; }
[MaxLength(50)]
public string Barcode { get; set; }
public string Size { get; set; }
public decimal Price { get; set; }
// One to Many for Product
[JsonIgnore]
public Product Product { get; set; }
}
Here you can serialise a Product and the JSON data will include the SKUs. This is the normal way of doing things.
However if you serialise a SKU you will NOT get it's parent product. Including the navigation property will send you into the dreaded loop and throw the "object cycle was detected" error.
I know this is limiting in some use cases but I would suggest you follow this pattern and if you want the parent object available you fetch it separately based on the child.
var parent = dbContext.SKUs.Include(p => p.Product).First(s => s.Sku == "MY SKU").Product
I fixed my API Core Net6.0 adding [JsonIgnore]:
public class SubCategoryDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Image { get; set; }
public int CategoryId { get; set; }
[JsonIgnore]
public Category Category { get; set; }
}
For net core 3.1 you have to add in Startup.cs:
services.AddMvc.AddJsonOptions(o => {
o.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
o.JsonSerializerOptions.MaxDepth = 0;
})
and import at least this package using nuget.org include prerelease:
<PackageReference Include="System.Text.Json" Version="5.0.0-rc.1.20451.14" />
following code is working for me in dotnet 5.0 :
services.AddControllersWithViews()
.AddJsonOptions(o => o.JsonSerializerOptions
.ReferenceHandler = ReferenceHandler.Preserve);
Finally fixed mine with System.Text.Json not NewtonSoft.Json using
var options = new JsonSerializerOptions()
{
MaxDepth = 0,
IgnoreNullValues = true,
IgnoreReadOnlyProperties = true
};
Using options to serialize
objstr = JsonSerializer.Serialize(obj,options);
My project built with a similar error.
Here's the code before
public class PrimaryClass {
public int PrimaryClassId
public ICollection<DependentClass> DependentClasses { get; set; }
}
public class DependentClass {
public int DependentClassId { get; set; }
public int PrimaryClassId { get; set; }
public PrimaryClass primaryClass { get; set; }
}
I took away the PrimaryClass object from the DependentClass model.
Code after
public class PrimaryClass {
public int PrimaryClassId
public ICollection<DependentClass> DependentClasses { get; set; }
}
public class DependentClass {
public int DependentClassId { get; set; }
public int PrimaryClassId { get; set; }
}
I also had to adjust the OnModelCreating method from
modelBuilder.Entity<PrimaryClass>().HasMany(p => p.DependentClasses).WithOne(d => d.primaryClass).HasForeignKey(d => d.PrimaryClassId);
to
modelBuilder.Entity<PrimaryClass>().HasMany(p => p.DependentClasses);
The DbSet query that's running is
public async Task<List<DependentClass>> GetPrimaryClassDependentClasses(PrimaryClass p)
{
return await _dbContext.DependentClass.Where(dep => dep.PrimaryClassId == p.PrimaryClassId).ToListAsync();
}
The error could have been with any of these 3 sections of code, but removing the primary object reference from the dependent class and adjusting the OnModelCreating resolved the error, I'm just not sure why that would cause a cycle.
In my case the problem was when creating the entity relationships. I linked the main entity using a foreign key inside the dependent entity like this
[ForeignKey("category_id")]
public Device_Category Device_Category { get; set; }
also I referred the dipendend entity inside the main entity as well.
public List<Device> devices { get; set; }
which created a cycle.
Dependent Entity
public class Device
{
[Key]
public int id { get; set; }
public int asset_number { get; set; }
public string brand { get; set; }
public string model_name { get; set; }
public string model_no { get; set; }
public string serial_no { get; set; }
public string os { get; set; }
public string os_version { get; set; }
public string note { get; set; }
public bool shared { get; set; }
public int week_limit { get; set; }
public bool auto_acceptance { get; set; }
public bool booking_availability { get; set; }
public bool hide_device { get; set; }
public bool last_booked_id { get; set; }
//getting the relationships category 1 to many
public int category_id { get; set; }
[ForeignKey("category_id")]
public Device_Category Device_Category { get; set; }
public List<Booking> Bookings { get; set; }
}
Main Entity
public class Device_Category
{
public int id { get; set; }
public string name { get; set; }
public List<Device> devices { get; set; }
}
}
So I commented the
public List<Device> devices { get; set; }
inside main entity (Device_Category) and problem solved

EF6 lazy loading not working

I have these objects:
public class Domain : EntityTypeConfiguration<Domain>, IEntity
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public Guid Guid { get; set; }
public ICollection<Website> Websites { get; set; }
}
public class Website: EntityTypeConfiguration<Website>, IEntity
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Range(1, int.MaxValue)]
public int DomainId { get; set; }
public string Description { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreationDate { get; set; }
[Required]
public Guid Guid { get; set; }
[Required]
public string LanguageIds { get; set; }
public bool AllowToSharedTemplates { get; set; }
public int PublishWebsiteId { get; set; }
[Required]
public string WebsiteUrl { get; set; }
public virtual Domain Domain { get; set; }
}
When I want all the websites I want the connected Domains as well (each website has one domain). But somehow this does not work.
public IList<T> GetAll()
{
IList<T> ret;
using (IocDbContext db = Context.CreateContext())
{
DbSet<T> dbSet = db.Set<T>();
ret = dbSet.ToList();
}
return ret;
}
The CreateContext
public IocDbContext CreateContext()
{
IocDbContext rety= new IocDbContext(_siteType.ConnectionString);
rety.Configuration.ProxyCreationEnabled = true;
return rety;
}
As you can see I have a generic repository. It works fine with just one object, but with navigation properties not. With lazy loading, it does not find the navigation property (domain in this case). I get this error:
The 'ObjectContent`1' type failed to serialize the response body for
content type 'application/json; charset=utf-8'.
When I try to map the DTO to an object:
public static Objects.Website ToModel(this Data.Contexts.Website value)
{
if (value == null)
return null;
return new Website
{
Id = value.Id,
Name = value.Name,
Domain = value.Domain?.ToModel()
};
}
Because you have your context wrapped in a using statement, lazy loading would never work because by the time you leave the GetAll() method, the context has been disposed and the connection to the database has been closed.
As much as lazy loading looks like a great option, I would highly recommend against using it unless you know what you're doing. It's much safer to explicitly load the data you need.

AutoMapper throwing StackOverflowException when calling ProjectTo<T>() on IQueryable

I have created classes using EF Code First that have collections of each other.
Entities:
public class Field
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<AppUser> Teachers { get; set; }
public Field()
{
Teachers = new List<AppUser>();
}
}
public class AppUser
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string UserName => Email;
public virtual List<Field> Fields { get; set; }
public AppUser()
{
Fields = new List<FieldDTO>();
}
}
DTOs:
public class FieldDTO
{
public int Id { get; set; }
public string Name { get; set; }
public List<AppUserDTO> Teachers { get; set; }
public FieldDTO()
{
Teachers = new List<AppUserDTO>();
}
}
public class AppUserDTO
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string UserName => Email;
public List<FieldDTO> Fields { get; set; }
public AppUserDTO()
{
Fields = new List<FieldDTO>();
}
}
Mappings:
Mapper.CreateMap<Field, FieldDTO>();
Mapper.CreateMap<FieldDTO, Field>();
Mapper.CreateMap<AppUserDTO, AppUser>();
Mapper.CreateMap<AppUser, AppUserDTO>();
And I am getting StackOverflowException when calling this code (Context is my dbContext):
protected override IQueryable<FieldDTO> GetQueryable()
{
IQueryable<Field> query = Context.Fields;
return query.ProjectTo<FieldDTO>();//exception thrown here
}
I guess this happens because it loops in Lists calling each other endlessly. But I do not understand why this happens. Are my mappings wrong?
You have self-referencing entities AND self-referencing DTOs. Generally speaking self-referencing DTOs are a bad idea. Especially when doing a projection - EF does not know how to join together and join together and join together a hierarchy of items.
You have two choices.
First, you can force a specific depth of hierarchy by explicitly modeling your DTOs with a hierarchy in mind:
public class FieldDTO
{
public int Id { get; set; }
public string Name { get; set; }
public List<TeacherDTO> Teachers { get; set; }
public FieldDTO()
{
Teachers = new List<TeacherDTO>();
}
}
public class TeacherDTO
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string UserName => Email;
}
public class AppUserDTO : TeacherDTO
{
public List<FieldDTO> Fields { get; set; }
public AppUserDTO()
{
Fields = new List<FieldDTO>();
}
}
This is the preferred way, as it's the most obvious and explicit.
The less obvious, less explicit way is to configure AutoMapper to have a maximum depth it will go to traverse hierarchical relationships:
CreateMap<AppUser, AppUserDTO>().MaxDepth(3);
I prefer to go #1 because it's the most easily understood, but #2 works as well.
Other option is using PreserveReferences() method.
CreateMap<AppUser, AppUserDTO>().PreserveReferences();
I use this generic method:
public static TTarget Convert<TSource, TTarget>(TSource sourceItem)
{
if (null == sourceItem)
{
return default(TTarget);
}
var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace, ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
var serializedObject = JsonConvert.SerializeObject(sourceItem, deserializeSettings);
return JsonConvert.DeserializeObject<TTarget>(serializedObject);
}
...
MapperConfiguration(cfg =>
{
cfg.ForAllMaps((map, exp) => exp.MaxDepth(1));
...
When you giving 1 navigation_property to 2nd entity and visa-versa it go in an infinite loop state. So, the compiler automatically throws a Stackoverflow exception.
So, to avoid that, you just need to remove one navigation_property from any of the entities.

Updating List<T> in DbContext

I have a Model like this
public class Challenge
{
public int ID { get; set; }
public string Name { get; set; }
public string Blurb { get; set; }
public int Points { get; set; }
public string Category { get; set; }
public string Flag { get; set; }
public List<string> SolvedBy { get; set; }
}
public class ChallengeDBContext : DbContext
{
public DbSet<Challenge> Challenges { get; set; }
}
and then Controller like this. But I cannot update the List "SolvedBy", the next time I step through with the debugger, the list is still empty.
[HttpPost]
public string Index(string flag = "", int id=0)
{
Challenge challenge = db.Challenges.Find(id);
if (flag == challenge.Flag)
{
var chall = db.Challenges.Find(id);
if (chall.SolvedBy == null)
{
chall.SolvedBy = new List<string>();
}
chall.SolvedBy.Add(User.Identity.Name);
db.Entry(chall).State = EntityState.Modified;
db.SaveChanges();
//congrats, you solved the puzzle
return "got it";
}
else
{
return "fail";
}
}
is there any way around it to make a list of strings kept in the database?
EF don't know how to store an array in database table so it just ignore it. You can create another table/entity or use XML/JSON to store the list. You can serialize the list before saving and deserialize it after loading from database
A List<T> in a model would normally map to a second table, but in your DbContext you only have a single table. Try adding a second table.
public class ChallengeDBContext : DbContext
{
public DbSet<Challenge> Challenges { get; set; }
public DbSet<Solution> Solutions {get; set;}
}
public class Challenge
{
public int ID { get; set; }
public string Name { get; set; }
public string Blurb { get; set; }
public int Points { get; set; }
public string Category { get; set; }
public string Flag { get; set; }
public List<Solution> SolvedBy { get; set; }
}
public class Solution
{
public int ID { get; set; }
public string Name { get; set; }
}
Then your controller can use code along the lines of...
var chall = db.Challenges.Find(id);
if (chall.SolvedBy == null)
{
chall.SolvedBy = new List<Solution>();
}
chall.SolvedBy.Add(new Solution {Name=User.Identity.Name});
None of the above has been tested and I may have made some mistakes there, but the general principle I want to illustrate is the fact that you need another table. The List<T> represents a JOIN in SQL.

Ria Services and navigation property issues

I'm encountering an issue using Silverlight4, Ria Services and Entity Framework.
From my sl client I try to get some data through ria services, in my domainService class this method gets called:
public IQueryable<LastMinuteWachtLijstPromotie> GetLastMinuteWachtLijstPromoties(){
IQueryable<LastMinuteWachtLijstPromotie> list = (IQueryable<LastMinuteWachtLijstPromotie>)this.ObjectContext.LastMinuteWachtLijstPromoties.Include("Promotie");
return (from LastMinuteWachtLijstPromotie lwmp in list where lwmp.Actief select lwmp);
}
when I check the contents of the list, in debug mode, it's filled with objects of type LastMinuteWachtLijstPromotie.
these objects have a navigation property to an Object named Promotie.
And i can access the properties of these Promotie objects.
On the silveright client however a method gets invoked when loading is complete:
public void OnLoadEntitiesCompleted(ServiceLoadResult<T> result) {
}
In this method I get all the requested LastMinuteWachtLijstPromotie objects as expected, the property
Promotie however is null.
I have set the [Include] tag on the property Promotie in the auto generated metadata class
and I use the .Include("Promotie")
These same methods are used for different objects from my Domain Model, this works perfectly.
Also, I cannot seem to find differences in the .edmx file with the database mappings and navigation properties.
Has anyone encountered the same issue or know a solution for it?
the metadata classes:
[MetadataTypeAttribute(typeof(LastMinuteWachtLijstPromotie.LastMinuteWachtLijstPromotieMetadata))]
public partial class LastMinuteWachtLijstPromotie
{
// This class allows you to attach custom attributes to properties
// of the LastMinuteWachtLijstPromotie class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class LastMinuteWachtLijstPromotieMetadata
{
// Metadata classes are not meant to be instantiated.
private LastMinuteWachtLijstPromotieMetadata()
{
}
public int AlertId { get; set; }
public string ArtikelNummer { get; set; }
public Nullable<int> ArtikelVariant { get; set; }
public int LastMinuteWachtLijstPromotieId { get; set; }
[Include]
public Promotie Promotie { get; set; }
public int PromotieArtikelId { get; set; }
public int PromotieId { get; set; }
public bool Actief { get; set; }
public DateTime Aanmaakdatum { get; set; }
}
}
[MetadataTypeAttribute(typeof(Promotie.PromotieMetadata))]
public partial class Promotie
{
// This class allows you to attach custom attributes to properties
// of the Promotie class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class PromotieMetadata
{
// Metadata classes are not meant to be instantiated.
private PromotieMetadata()
{
}
public string ActieType { get; set; }
public string AssortimentsManagerNaam { get; set; }
public string AssortimentsManagerTeamIds { get; set; }
[Display(Name = "Commerciele tekst")]
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Nokavision.ReclameFolder.UI.Web.Resources.ValidationResources))]
public string CommercieleTekst { get; set; }
[Display(Name = " ")]
public string CommercieleTekstDetails { get; set; }
[Include]
public Frame Frame { get; set; }
public Nullable<int> FrameId { get; set; }
public Nullable<DateTime> LastMinuteWijzigingsDatum { get; set; }
public string Opmerkingen { get; set; }
[Display(Name = "Op wachtlijst")]
public Nullable<bool> OpWachtLijst { get; set; }
//public Nullable<int> PromotieCopyId { get; set; }
public int PromotieId { get; set; }
[Include]
public EntityCollection<PromotieLeverancier> PromotieLeveranciers { get; set; }
[Include]
public EntityCollection<PromotieMutatie> PromotieMutaties{ get; set; }
//public Nullable<int> PromotieOrigineleId { get; set; }
[Include]
public EntityCollection<PromotieSymbool> PromotieSymbolen { get; set; }
public string Status { get; set; }
[Display(Name = "Promotie inhoud")]
public string PromotieInhoud { get; set; }
[Display(Name = "Promotie eenheid")]
public string PromotieEenheid { get; set; }
[Display(Name = "Promotie prijs")]
public decimal PromotiePrijs { get; set; }
}
}
Add the Composition attribute to the property Promotie property of the LastMinuteWachtLijstPromotieMetadata class. Then it should work.
public partial class LastMinuteWachtLijstPromotie {
internal sealed class LastMinuteWachtLijstPromotieMetadata{
[Include]
[Composition]
public Promotie Promotie { get; set; }
}
}
I know this is an older thread and it may well have been answered elsewhere but I just stumbled upon it and since nobody has provided a link or a better answer.
I'm currently using Silverlight 5 and this is what worked for me (I think the process is the same in SL4 IIRC).
When propegating navigation properties to the client you need to tell RIA services that there is a relationship somewhere using the [Key] and [Association] attributes, this, not unlike the entity framework just describes how to map the relationship to the proper object.
First the metadata classes:
[MetadataTypeAttribute(typeof(Category.CategoryMetadata))]
public partial class Category
{
internal sealed class CategoryMetadata
{
private CategoryMetadata() {
}
[Key]
public int Id { get; set; }
public string NAME { get; set; }
[Association("CategoryToProducts", "Id", "CAT")]
[Include]
public EntityCollection<Product> Products { get; set; }
}
}
[MetadataTypeAttribute(typeof(Order.OrderMetadata))]
public partial class Order
{
internal sealed class OrderMetadata
{
// Metadata classes are not meant to be instantiated.
private OrderMetadata() {
}
[Key]
public int Id { get; set; }
public int PRODID { get; set; }
public DateTime DATE { get; set; }
public bool DONE { get; set; }
public int QTY { get; set; }
[Association("OrderToProduct", "PRODID", "Id", IsForeignKey = true)]
[Include]
public Product Product { get; set; }
}
}
[MetadataTypeAttribute(typeof(Product.ProductMetadata))]
public partial class Product
{
internal sealed class ProductMetadata
{
private ProductMetadata() {
}
[Key]
public int Id { get; set; }
public int CAT { get; set; }
public string NAME { get; set; }
public string DESC { get; set; }
public decimal PRICE { get; set; }
public int QTY { get; set; }
public long UPC { get; set; }
[Association("ProdToCat", "CAT", "Id", IsForeignKey = true)]
[Include]
public Category Category { get; set; }
[Association("ProductToOrders", "Id", "PRODID")]
[Include]
public EntityCollection<Order> Orders { get; set; }
}
}
Now we need to tell RIA services we want it to load the association:
(Note: Intellisense says it's a dot separated list of property names to include, however I tried something like .Include("Category.SubCategory") and this failed with an exception... though .Include("Category").Include("SubCategory") worked like a charm!)
public IQueryable<Product> GetProducts() {
return this.ObjectContext.Products.Include("Category");
}
I can now access my "Category" property from the Silverlight client and it is not NULL :)
Same as SilverX: just had the issue, solved it and thought it could be useful to someone.
I too had all the configuration stuff correct ([Include] for RIA S, Include() for EF) but a navigation property was still null on the Silverlight side.
Turns out the domain service method was using the [Invoke] attribute (and returning a IEnumerable<T>). Removing this attribute solved the issue.
(just for the record, [Invoke] was being used because the method had a List<Entity> parameter)

Categories

Resources