I'm trying to make an optional relationship between to entities, but I'm having trouble finding the correct syntax. Actually I need a 0..1 to 0..2 relationship, but I'm guessing that once I find the way to do the 0..1 to 0..1 that will be trivial.
Simplified what I have is this:
class Foo
{
int Id { get; set; }
//navigation property
virtual Bar Bar { get; private set; }
}
class Bar
{
int Id { get; set; }
int? LeftFooId { get; set; }
[ForeignKey("LeftFooId")]
Foo LeftFoo { get; set; }
int? RightFooId{ get; set; }
[ForeignKey("RightFooId")]
Foo RightFoo { get; set; }
}
A Foo can be connected to zero or one Bar, never more. A Bar can have a LeftFoo and a RightFoo, or one or neither. The Bar property of Foo should be null if it is not referenced by a Bar, and it should contain the Bar that references it when it is referenced by a Bar.
With the code above the Bar references the Foo correctly, but EF gives the Foo table a Bar_Id and the Bar property is always null.
I've tried several different ways to setup these entity classes and I've used differenct Fluent API calls to make this work, but I don't get the wanted results.
Try this:
public class Foo
{
public int Id { get; set; }
public virtual Bar Bar { get; set; }
}
public class Bar
{
public int Id {get; set;}
public virtual Foo LeftFoo {get;set;}
public virtual Foo RightFoo {get;set;}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Bar>()
.HasOptiona(b => b.LeftFoo)
.WithOptionalDependent()
.Map(k = k.MapKey("LeftFooId"));
modelBuilder.Entity<Bar>()
.HasOptional(b => b.RightFoo)
.WithOptionalDependent()
.Map(k => k.MapKey("RightFooId"));
...
}
Result
Edit for seeding
I would do the following, didn't test the code, but it should work:
var bars = new List<Bar>();
bars.Add(new Bar());
bars.Add(new Bar());
...//this means as many as you need/want
bars.ForEach(b => context.Bars.AddOrUpdate(b));
context.SaveChanges();
var leftFoos = new List<Foo>();
leftFoos.Add(new Foo());
leftFoos.Add(new Foo());
...//this means as many as you need/want
leftFoos.ForEach(f => context.Foos.AddOrUpdate(f));
context.SaveChanges();
var rightFoos = new List<Foo>();
rightFoos.Add(new Foo());
rightFoos.Add(new Foo());
...//this means as many as you need/want
rightFoos.Foreach(f => context.Foos.AddOrUpdate(f));
context.SaveChanges();
int i=0;
foreach(var bar in bars)
{
bar.LeftFoo = leftFoos.ElementAt(i);
bar.RightFoo = rightFoos.ElementAt(i);
i++;
}
context.SaveChanges();
For simplicity bars, leftFoos and rightFoos have the same number of elements.
I modified the code like this:
class Foo
{
int Id { get; set; }
int? BarId { get; set; }
[ForeignKey("BarId")]
virtual Bar Bar { get; set; }
}
class Bar
{
int Id { get; set; }
int? LeftFooId { get; set; }
[ForeignKey("LeftFooId")]
Foo LeftFoo { get; set; }
int? RightFooId{ get; set; }
[ForeignKey("RightFooId")]
Foo RightFoo { get; set; }
}
I may be wrong, but automatic assignment of a pair of FK's my be impossible, at least from the Foo to the Bar: How would EF know if it should assign the FK to the LeftFoo or the RightFoo. So for now I'm just doing both assignments manually:
Foo myFirstFoo = new Foo();
Foo mySecondFoo = new Foo();
Bar myBar = new Bar();
context.Add(myFirstFoo); context.Add(mySecondFoo); context.Add(myBar);
context.SaveChanges();
myFirstFoo.BarId = myBar.Id;
myBar.LeftFooId = myFirstFoo.Id;
mySecondFoo.BarId = myBar.Id;
myBar.RightFooId = mySecondFoo.Id;
context.Update(myFirstFoo); context.Update(mySecondFoo); context.Update(myBar);
context.SaveChanges();
This seems to work, but I do hope it can be done better.
Try make your properties virtual and use POCO proxies (using Create instead of new ...). This would ensure the automatic update on the side properties. i.e when assigning a Foo to a Bar the POCO proxy will assign the corresponding Bar to the Foo but, ... and this should be the main problem: what do you expect if assign a Bar to a Foo? Which property do you want to be automatically assigned, left of right? Annotate the Bar property [InverseProperty("...")] to indicate this and think about you may need another property of type Bar in Foo for the other relationship.
Hope this helps
Related
I am totally not getting this, because I have used this library in Xamarin apps for several years.
I have this base class that contains properties common in all db items:
public class BaseItem
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; } = 0; // SQLite ID
public long CreatedTimeSeconds { get; set; } = DateTime.Now.ToUnixTimeSeconds();
public long ModifiedTimeSeconds { get; set; } = DateTime.Now.ToUnixTimeSeconds();
}
Now, I derive from it:
[Table("CategoryTable")]
public class Category : BaseItem
{
public int CategoryTypeID { get; set; } = (int)CategoryType.Invalid;
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
}
Here's a simplified version of what I'm seeing:
public class DBWorld
{
ISQLiteService SQLite { get { return DependencyService.Get<ISQLiteService>(); } }
private readonly SQLiteConnection _conn;
public DBWorld()
{
_conn = SQLite.GetConnection("myapp.sqlite");
}
public void TestThis()
{
_conn.CreateTable<Category>();
var category = new Category();
category.Name = "This Should Work";
int recCount = connection.Insert(category);
// at this point recCount shows as 1, and category.ID shows as zero.
// I thought Insert was supposed to set the autoincrement primary key
// regardless, it should be set in the database, right? So...
var categoryList = connection.Query<Category>($"SELECT * FROM {DBConstants.CategoryTableName}");
// at this point categoryList[0] contains all the expected values, except ID = 0
}
}
I am obviously missing something, but for the life of me, I can't figure out what...
Like so many other bizarre things that happen in the Visual Studio Xamarin world, when I went back later, this worked the way all of us expect. I guess Visual Studio was just tired and needed to be restarted.
I have two tables (NPG_Chemical and NPG_Chemical_Synonym):
public partial class NPG_Chemical
{
[Key]
[Column(TypeName = "numeric")]
public decimal NPG_Chemical_ID { get; set; }
[StringLength(256)]
public string Chemical { get; set; }
}
public partial class NPG_Chemical_Synonym
{
[Key]
[Column(TypeName = "numeric")]
public decimal NPG_Chemical_Synonym_ID { get; set; }
[ForeignKey("NPG_Chemical_ID")]
[Column(TypeName = "numeric")]
public decimal NPG_Chemical_ID { get; set; }
[StringLength(512)]
public string Synonym { get; set; }
}
In the NPG_ChemicalController I have something like:
[HttpPost]
public ActionResult Create(NPG_ChemicalViewModel model)
{
using (var context = new NPG_Model())
{
var chemical = new NPG_Chemical();
chemical.Chemical = model.NPG_Chemical.Chemical;
context.NPG_Chemical.Add(chemical);
var synonym = new NPG_Chemical_Synonym();
synonym.Synonym = model.NPG_Chemical_Synonym.Synonym;
synonym.NPG_Chemical_ID = chemical.NPG_Chemical_ID;
context.NPG_Chemical_Synonym.Add(synonym);
context.SaveChanges();
}
return View();
}
and NPG_ChemicalViewModel:
namespace NPG_Administrative_Utility.Models
{
public class NPG_ChemicalViewModel
{
public NPG_ChemicalViewModel()
{
NPG_Chemical = new NPG_Chemical();
NPG_Chemical_Synonym = new NPG_Chemical_Synonym();
}
public NPG_Chemical NPG_Chemical { get; set; }
public NPG_Chemical_Synonym NPG_Chemical_Synonym { get; set; }
}
}
When I try to create a view based on NPG_ChemicalViewModel, it shows:
Can any one help me on this?
You'll need a view model. At the simplest, you can just do something like:
public class NPG_ChemicalViewModel
{
public NPG_ChemicalViewModel()
{
NPG_Chemical = new NPG_Chemical();
NPG_Chemical_Synonym = new NPG_Chemical_Synonym();
}
public NPG_Chemical NPG_Chemical { get; set; }
public NPG_Chemical_Synonym NPG_Chemical_Synonym { get; set; }
}
Then, change your action to accept this:
public ActionResult Create(NPG_ChemicalViewModel model)
In your view, you would generate the individual properties like:
#Html.EditorFor(m => m.NPG_Chemical.Chemical)
However, it's far better to only include the properties on your view model that you want to be edited:
public class ChemicalViewModel
{
public string Chemical { get; set; }
public string Synonym { get; set; }
}
Then, in your action, you just map this posted data where it should go:
var chemical = new NPG_Chemical();
chemical.Chemical = model.Chemical;
context.NPG_Chemical.Add(chemical);
var synonym = new NPG_Chemical_Synonym();
synonym.Synonym = model.Synonym;
synonym.NPG_Chemical_ID = chemical.NPG_Chemical_ID;
context.NPG_Chemical_Synonym.Add(synonym);
That said, there's some significant issues with your code here. First, it looks like you're dealing with a one-to-one or one-to-many relationship here between Chemical and Synonym, but right now, you have no foreign keys being utilized. You should add a navigation property to your synonym class:
[ForeignKey("NPG_Chemical_ID")]
public NPG_Chemical Chemical { get; set; }
That tells Entity Framework that you have a relationship and among other things allows it to automatically fill in IDs as necessary. For example, with that, you could now simply do:
synonym.Chemical = chemical;
Instead of directly referencing the ID. That way, if the id is autogenerated or otherwise unknown before saving, the relationship will still be preserved. Whereas, without it, you'd have to save chemical first, set the autogenerated id on synonym and then save the synonym in a separate transaction.
Second, if you're going to use keys typed as "numeric". Then, you're going to be responsible for generating a unique numeric string for each record. That's a huge pain, as it's going to require checking a proposed id against other existing record ids before actually saving. Otherwise, you run the risk of a primary key collision. It's far better to use a standard autoincrementing PK or barring that, at least a GUID, where you're assured a reasonably low risk of collisions occurring.
Third, you should absolute not use using with your context. Here it's not a big deal, since, you're only saving and not reading data from the database, but in a typical view, lazy-loading will kick you in the posterior quick doing that. Your context should be request-scoped, either as an instance variable on your controller (since the controller is newed up and disposed with each request) or using dependency injection. You never want to create an instance of your context anywhere else, including an action method.
First of all, this is not exactly a duplication of the dozens of other posts and I have tried all of them and none of them work.
I have a model that contains many more values than my web api consumers need.
public class Publication
{
[Key]
public int PublicationID { get; set; }
public string PublicationTitle { get; set; }
public string Frequency { get; set; }
public DateTime NextIssueDate { get; set; }
public DateTime SpaceDeadline { get; set; }
public DateTime MaterialsDeadline { get; set; }
public DateTime CreatedDt { get; set; }
public string CreatedBy { get; set; }
public DateTime UpdatedDt { get; set; }
public string UpdatedBy { get; set; }
}
I only want say a few of the fields to be passed in the API. I've tried this code but instead of leaving out say UpdateBy in the Json result, it returns it with a null value. How do I get rid of that? I've tried several dozen variations but they either fail to compile or fail to return results.
public IQueryable<Publication> GetPublications()
{
return db.Publications
.ToList()
.Select(p => new Publication {
PublicationID = p.PublicationID,
PublicationTitle = p.PublicationTitle,
Frequency = p.Frequency,
NextIssueDate = p.NextIssueDate
})
.AsQueryable();
}
Don't serialize your DAO. Create a complete contract and then serialize it selectively. To creating different contracts for different cases, you could simplify it using Json.Net; you could just create a custom contract resolver and use it as a parameter of SerializeObject() like so
static void Main(string[] args)
{
var person = new TestContract {FirstName = "John", LastName = "Doe", Age = 36};
var firstNameContract = new SelectiveSerializer("firstname");
var allPropertiesContract = new SelectiveSerializer("firstname, lastname, age");
var allJson = JsonConvert.SerializeObject(
person,
Formatting.Indented,
new JsonSerializerSettings {ContractResolver = allPropertiesContract});
var firstNameJson = JsonConvert.SerializeObject(
person,
Formatting.Indented,
new JsonSerializerSettings {ContractResolver = firstNameContract});
Console.WriteLine(allJson);
// {
// "FirstName": "John",
// "LastName": "Doe",
// "Age": 36
// }
Console.WriteLine(firstNameJson);
// {
// "FirstName": "John",
// }
}
public class SelectiveSerializer : DefaultContractResolver
{
private readonly string[] _fields;
public SelectiveSerializer(string fields)
{
var fieldColl = fields.Split(',');
_fields = fieldColl
.Select(f => f.ToLower().Trim())
.ToArray();
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = o => _fields.Contains(member.Name.ToLower());
return property;
}
}
public class TestContract
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
Without much effort, you could probably work this into your default mediatype formatter (in the pipeline) to look for a parameter in the request called 'fields' or whatever and then use the custom contract resolver if present, and then it would be seamless default behavior to limit fields if specified or serialize the entire object if not specified.
On the academic side, here is the justification:
Any modification to the data is considered a "view concern" which means, in an API, it should controlled by query parameters and accept header. In this case, the "representation" of the data is application/json and you've chose to "filter" the returned fields. All of this can (and should be, imo) be handled during serialization. So your "model" in this case will always be the full model vs. some subset of the model. The full model in this example contains first name, last name, and age. In reality, this could be hundreds of properties. If you want to allow the client to choose a subset of the complete model, this is how you could do it with selective serialization.
You can similar behaviors in graph apis. There, the default for large models is that you get an empty object if you don't specify fields, forcing the client to be very specific about what it asks for, which is great when payload size matters (e.g. mobile applications). And, there's nothing stopping from creating field presets like 'name' which could mean 'firstname, lastname' or 'all' which includes all properties.
I've never been a fan of having hundreds of data objects that all serve some ad hoc requirement for a data set that is used in 20 different contexts where some cases require more data while others require less. IMO if you have to go through the same process to get the data, whether it complete or not, you shouldn't waste your time creating additional objects to frame the data for the sake of the client, and this should help you achieve that.
It's because you're returning a collection of Publication objects so you will get every property that is contained in that class, whether you populate it or not. If you want to return a subset of the properties then create a class that has only the properties you want to return and create an instance of that class in your query.
public IQueryable<WhatIReallyWantToReturn> GetPublications()
{
return db.Publications
.ToList()
.Select(p => new WhatIReallyWantToReturn {
PublicationID = p.PublicationID,
PublicationTitle = p.PublicationTitle,
Frequency = p.Frequency,
NextIssueDate = p.NextIssueDate
})
.AsQueryable();
}
private class WhatIReallyWantToReturn
{
public int PublicationID { get; set; }
public string PublicationTitle { get; set; }
public string Frequency { get; set; }
public DateTime NextIssueDate { get; set; }
}
using Newtonsoft.Json;
public class Publication
{
[Key]
public int PublicationID { get; set; }
public string PublicationTitle { get; set; }
public string Frequency { get; set; }
public DateTime NextIssueDate { get; set; }
public DateTime SpaceDeadline { get; set; }
public DateTime MaterialsDeadline { get; set; }
[JsonIgnore]
public DateTime CreatedDt { get; set; }
[JsonIgnore]
public string CreatedBy { get; set; }
[JsonIgnore]
public DateTime UpdatedDt { get; set; }
[JsonIgnore]
public string UpdatedBy { get; set; }
}
as Craig W. said you can use viewmodel ,also you can use anonymous type
(notice viewmodel is better way because you can use some utilities like automapper for mapping your property automatically)
JsonIgnore annotation has worked for me
[JsonIgnore]
public int Ranking { get; set; }
Here is a great article (Dec 2019) on the subject. It offers a solution for data shaping by making use of ExpandoObject and Type Reflection. The properties that the client requires can then be passed through the request as a query parameter (i.e. separated by a comma). The article also offers solution to the JSON Serialization problem.
Startup.cs file:
services.AddControllers(config =>
{
config.RespectBrowserAcceptHeader = true;
config.ReturnHttpNotAcceptable = true;
})
.AddXmlDataContractSerializerFormatters()
.AddNewtonsoftJson();
+1 for Sinaesthetic's answer.
I just finished reading an article, about GraphQL which solves exactly this problem. You can define exactly which fields do you need in the same request. No need for creating new endpoints every single time, when the caller needs just a specific subset of the properties.
If you can do this in .NET WEB API too without creating new models and endpoints, with just a very little extra effort, why wouldn't you (instead of exchanging Web Api for GraphQL).
Actually his SelectiveSerializer could be upgarded with reflection, so if you want to define which props you need in
C#, you can do this by providing property expressions, so you don't have to worry about misstyping prop names.
I bet there are other solutions for this, but the basic concept is the most important that we can define which fields we need in our json without creating new models.
I have a bit of a twist that is a little more trouble than I thought it would be. Normally I would have an InArgument and use it as below:
public InArgument<Int32> XYZ_ID { get; set; }
public InArgument<Int32> XYZ_COUNT { get; set; }
protected override IAsyncResult BeginExecute(...)
{
....snip....
_ABC_ID = XYZ_ID.Get(context),
_ABC_Count = XYZ_COUNT.Get(context)
This works great and I thought a custom object we be close to the same process but I can't seem to figure it out. ActvUsrPrgmResults is just a class of properties such as AccountName, FirstName etc. So I passed it in like any other type.
public InArgument<bool> IsHappy { get; set; }
public InArgument<bool> IsClapping { get; set; }
public InArgument<ActvUsrPrgmResults> itm_ActvUsrPrgm { get; set; }
Accessing it though is a bit more difficult....for me.
protected override void Execute(CodeActivityContext context)
{
NewPerson x = new NewPerson
{
AccountName = this.itm_ActvUsrPrgm.Get(?????
//this doesn't work either
AccountName = itm_ActvUsrPrgm.?????
In other words I can't see how to access the properties of the itm_ActvUsrPrgm InArgument.
Thank You for any help or direction
JB
Additional Info
I have this CodeActivity in a ForEach (List). So each item in the generic collection is a single instance of ActvUsrPrgmResults. So I hand this off to my CodeActivity thinking I will have a handle to manipulate that item's data????
Interesting
Now based upon Will's comments I got to thinking about this slight of hand. It works but shouldn't there be a more elegant approach?
public InArgument<bool> IsHappy { get; set; }
public InArgument<bool> IsClapping { get; set; }
public InArgument<ActvUsrPrgmResults> itm_ActvUsrPrgm { get; set; }
protected override void Execute(CodeActivityContext context)
{
ActvUsrPrgmResults y = itm_ActvUsrPrgm.Get(context);
NewPerson x = new NewPerson
{
AccountName = y.AccountName....
The problem is me, myself, and I. I wasn't getting a handle on the object being passed in. For some under the covers reason an "InArgument" is not immediately accessible until you get a firm grasp on the exact object from the given context. I don't know for sure but I suspect this is due to multiple workflows running so you can't just grab any ole object ytou must get the object from the proper context. Anyway here are my comments of what I learned inline.
public InArgument<bool> IsHappy { get; set; } //bool variable being passed in
public InArgument<bool> IsClapping { get; set; } //bool variable being passed in
public InArgument<ActvUsrPrgmResults> itm_ActvUsrPrgm { get; set; } //custom object being passed in
protected override void Execute(CodeActivityContext context)
{
bool Happy = context.GetValue(this.IsHappy);
bool Clap = context.GetValue_this.IsClapping);
ActvUsrPrgmResults y = context.GetValue(this.itm_ActvUsrPrgm);
//NOW!!! we have a handle to the proper objects for this context
//This also works. I just flip flopped the InArgument property and the context.
ActvUsrPrgmResults y = itm_ActvUsrPrgm.Get(context);
NewPerson x = new NewPerson
{
AccountName = y.AccountName....
I've having trouble figuring out how to select based on a list in a many to many relationship.
I've created with entities-framework the following entities and many to many relationship (please correct me if I'm going about this wrong) :
public class Foo {
public int FooID { get; set; }
public string FooName { get; set; }
public virtual ICollection<FooBar> Foo_Bar { get; set; }
}
public class Bar {
public int BarID { get; set; }
public string BarName { get; set; }
public virtual ICollection<FooBar> Foo_Bar { get; set; }
}
public class FooBar {
public int FooBarID{ get; set; }
public virtual int BarID { get; set; }
public virtual int FooID { get; set; }
}
In my code my controller will receive a list of Foo and I need to find all the Bar with those foo ( both with only and with any )
I'm at a loss for where to start really... this is all I've come up with:
public PartialViewResult SearchAnyBar(List<Foo> foos) {
List<FooBar> foobars = _db.FooBar.Select(fb => fb).ToList<FooBar>();
List<Bar> searchedBars = new List<Bar>();
foreach (Foo f in foos)
{
foreach (FooBar fXb in foobars)
{
if (fXb.FooID == f.FooID)
{
searchedBars.Add(_db.Bar.Where(b => b.BarID == fXb.BarID).FirstOrDefault());
}
}
}
return PartialView("The View", searchBars);
}
This works for the grabbing any Bar however:
I'm pretty positive there's a much better way of doing this, is there a way to select based on a list instead of going about 2 foreach loop?
I'm not sure how to go about getting a list of Foos where the Foo has ALL the Bars and not just ANY.
Remove FooBar class.
Just create a public virtual ICollection<Foo> Foos {get;set;} in you Bar class
and a public virtual ICollection<Bar> Bars {get;set;} in your Foo Class
This will create a many to many relationship (with a relation table named [Foo-Bar] or something like that in your db... but who minds, you will be using objects).
then
any query :
var listOfFooId = <a list of Foo>.Select(m => m.FooId).ToList;
return _db.Bar.Where(m => m.Foos.Any(x => listOfFooId.Contains(x.FooId)));
not sure I understood well the "only" and "any", but if you have problems with the other query... ask.
Untested, but it looks like you just need a join here... Joining on all the Bars off of all the FooBars joined to the Foos passed in, right?
public PartialViewResult SearchAnyBar(List<Foo> foos) {
var bars = (from f in foos
join fb in _db.FooBars on f.Id equals fb.FooId
join b in _db.Bars on fb.BarId equals b.BarId
select b).ToList();
return PartialView("The View", bars);
}