How to auto assign a foreign key? - c#

I am using SQLite Browser and Dapper. How to auto assign a foreign key from one table to another? I am trying to save a course object's column into the Course table. And how to assign the Course table column PersonID to the Person table's column Person ID?
My thought was to pass in a PersonModel object and a CourseModel object and somehow use the execute method to assign the course.PersonID column. But I can't get it to work.
internal class Program
{
private static void Main(string[] args)
{
PersonModel person = new PersonModel("Jeff");
CourseModel course = new CourseModel("History");
DBAccess.SavePerson(person);
DBAccess.AddCourse(person, course);
Console.ReadLine();
}
}
public class PersonModel
{
public int PersonID { get; set; }
public string Name { get; set; }
public PersonModel(string name)
{
Name = name;
}
}
public class CourseModel
{
public int CourseID { get; set; }
public string CourseName { get; set; }
public int PersonID { get; set; }
public CourseModel(string name)
{
CourseName = name;
}
}
public class DBAccess
{
public static string LoadConnectionString(string id = "Default")
{
return ConfigurationManager.ConnectionStrings[id].ConnectionString;
}
public static void SavePerson(PersonModel person)
{
using (IDbConnection cnn = new SQLiteConnection(LoadConnectionString()))
{
cnn.Execute("insert into Person (Name) values (#Name)", person);
}
}
public static void AddCourse(PersonModel person, CourseModel course)
{
// I want to log the vales of the course and somewhow assign the "person personid" to the "course personid" which is set as the foreign key in SQLite Browser.
using (IDbConnection cnn = new SQLiteConnection(LoadConnectionString()))
{
cnn.Execute("insert into Course (CourseName) values (#CourseName)", course);
}
}
}

Should just be:
cnn.Execute("Insert into Course (CourseName, PersonId) values
(#CourseName,#PersonId)", new { CourseName = course.CourseName, PersonId =
person.PersonId)`
If Course has its person Id already populated, no need to send it through the method, just use it in the query above.

Related

How do you load data to a list that has a foreign key that matches the current objects primary key?

I am looking to save a list of entries that has the CardID (foreign key) the same as the card (ID) that I am currently accessing. And then I want to be able to load a list of entry where the foreign key matched the card ID that I am accessing.
C#:
public class CardModel
{
public int ID { get; set; }
public int Month { get; set; }
public int Day { get; set; }
public List<EntryModel> Enteries { get; set; } = new List<Enteries>();
}
public class EntryModel
{
public int ID { get; set; }
public string Entry { get; set; }
// Foreign Key
public int CardModelID { get; set; }
}
CardModel card = new CardModel();
SQLite:
public static SaveCard(CardModel card)
{
using(IDBConnection cnn = new SQLiteConnection(LoadConnectionString()))
{
cnn.Execute("insert into Card (Month, Day) values (#Month, #Day)", card);
}
}
public static SaveEntry(EntryModel)
{
using(IDBConnection cnn = new SQLiteConnection(LoadConnectionString()))
{
cnn.Execute("insert into EntryModel Entry values #Entry", entry);
}
}
// Load a list of entries that have a matching (Foreign/Primary key) to the object I //pass in
// Not sure if I am on the right track with this???
public static List<EntryModel> LoadEntries(CardModel card)
{
using(IDBConnection cnn = new SQLiteConnection(LoadConnectionString()))
{
var output = cnn.Query<EntryModel>("select * from Entry where CardID = card.ID", card);
return output.ToList();
}
}

Nested collections in CSV Helper mapping

I have a collection Car including fields for date of production, car name, production place - this is a new model named ProductionPlace including City, Address and Id, and there is another collection in Cars which is Creator - including name, surname and country.
So this basically looks like this
Cars
{
ProductionDate,
Name,
ProductionPlace
{
Id,
City,
Address
},
Creator
{
Name,
Surname,
Country
}
How can I map those using CSV Helper?
I'm getting "there is no field named Creator" for example. I can simply map fields but not collection nested in my main collection
Edit
My code as far looks like that
public void MapCar()
{
Map(mapping => mapping.Car).ConvertUsing(
row => { List<Car> car = new List<Car>
{
ProductionDate = row.GetField("ProductionDate"),
Name = row.GetField("Name"),
ProductionPlace = new ProductionPlace
{
Id = row.GetField("Id"),
City = row.GetField("City"),
Address = row.GetField("Address")
},
Creator = new Creator
{
Name = row.GetField("Name"),
Surname = row.GetField("Surname"),
Country = row.GetFiele("Country")
}
};)}
Both Creator and ProductionPlace are separate models. And Car is a model using those two as it's fields.
Really the only issue you have right now is that both Car and Creator have the same name for the property Name. If you use the CsvHelper name attribute, you can give them different names in your CsvHeader and the rest will map without you having to do anything else.
void Main()
{
var input = new StringBuilder();
input.AppendLine("ProductionDate,CarName,Id,City,Address,CreatorName,Surname,Country");
input.AppendLine("2021-06-03,Tesla,1,MyCity,MyAddress,Bob,Jones,MyCountry");
using (var reader = new StringReader(input.ToString()))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = csv.GetRecords<Car>().Dump();
}
}
public class Car
{
public DateTime ProductionDate { get; set; }
[Name("CarName")]
public string Name { get; set; }
public ProductionPlace ProductionPlace { get; set; }
public Creator Creator { get; set; }
}
public class ProductionPlace
{
public int Id { get; set; }
public string City { get; set; }
public string Address { get; set; }
}
public class Creator
{
[Name("CreatorName")]
public string Name { get; set; }
public string Surname { get; set; }
public string Country { get; set; }
}

SQLite-Net Extensions how to correctly update object recursively

I am using SQLite-Net PCL together with SQLite-Net extensions for the development of an application using Xamarin.
I have a one to many relationship between two classes A and B defined as follows:
public class A
{
[PrimaryKey, AutoIncrement]
public int Id
{
get;
set;
}
public string Name
{
get;
set;
}
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<B> Sons
{
get;
set;
}
public A()
{
}
public A(string name, List<B> sons)
{
Name = name;
Sons = sons;
}
}
public class B
{
[PrimaryKey, AutoIncrement]
public int Id
{
get;
set;
}
public string Name
{
get;
set;
}
[ForeignKey(typeof(A))]
public int FatherId
{
get;
set;
}
[ManyToOne]
public A Father
{
get;
set;
}
public B()
{
}
public B(string name)
{
Name = name;
}
}
What I would like to do, is to retrieve an object of type A from the database, remove one of the Sons objects of type B and update the database accordingly. This is what I have tried:
var sons = new List<B>
{
new B("uno"),
new B("due"),
new B("tre"),
};
one = new A("padre", sons);
using (var conn = DatabaseStore.GetConnection())
{
conn.DeleteAll<A>();
conn.DeleteAll<B>();
conn.InsertWithChildren(one, true);
A retrieved = conn.GetWithChildren<A>(one.Id);
retrieved.Sons.RemoveAt(1);
}
using (var conn = DatabaseStore.GetConnection())
{
var retrieved = conn.GetWithChildren<A>(one.Id);
retrieved.Sons.RemoveAt(1); //"due"
//conn.UpdateWithChildren(retrieved);
conn.InsertOrReplaceWithChildren(retrieved, true);
}
The problem is that both with UpdateWithChildren and with InsertOrReplaceWithChildren the the object is not really removed from the database, but only it's foreign key nulled. Is it possible to make it delete the son object?
You're not really trying to delete any object at all. You're just removing the relationship between the two objects, but nothing stops you from having more objects related to any of them, so deleting any is not correct as you may break other relationships.
It should be more like this:
using (var conn = DatabaseStore.GetConnection())
{
var retrieved = conn.GetWithChildren<A>(one.Id);
var due = retrieved.Sons[1];
// This is not required if the foreign key is in the other end,
// but it would be the usual way for any other scenario
// retrieved.Sons.Remove(due);
// conn.UpdateWithChildren(retrieved);
// Then delete the object if it's no longer required to exist in the database
conn.delete(due);
}

How can I map 2 non-hierarchical classes to the same table with a defining column & value in Entity Framework?

So there is a way to map 2 hierarchical classes in EF to the same table with a defining column and value (http://msdn.microsoft.com/en-us/data/jj591617.aspx#2.4). I am wondering if there's a way to map 2 non-hierarchical classes to the same table in the same manner.
Example:
class User
Guid Id
string Name
Guid? GroupId
class Group
Guid Id
string Name
Table
uid Id PK
varchar Name
bit IsGroup
uid GroupId nullable
I can't change the schema of that table, so the only solution I've come up with so far is to create a view for User and a view for Group.
Mo,
I think that at the end of the day you are after a user group that contains the users that belong to it. If that's the case here is something that will get you started. Remember that this is not meant to be a "best practice" just an example to get you up and running. you will need to tweak this code for your exact situation.
Here's the basic jest of it. You need to give group a property of type List...then the populate the list of users from a database query. In the below example I wrote a method that handles populating the list. However in practice you don't have to do populate the list here you could instantiate the userGroup and populate the list of users from inside an action method. Like I said this in not a best practice just a quick example.
Here is an example.
public class userGroup
{
class user
{
private int id { get; set; }
private string name { get; set; }
private Guid grpId { get; set; }
public int userId { get { return id; } set { id = value; } }
public string userName { get { return name; } set { name = value; } }
public Guid groupId { get { return grpId; } set { grpId = value; } }
public user() { }
}
class group
{
private Guid id { get; set; }
private string name { get; set; }
public List<user> usersInGroup = new List<user>();
public Guid groupId { get { return id; } set { id = value; } }
public string groupName { get { return name; } set { name = value; } }
public group() { }
}
public userGroup() { }
public group getUserGroup()
{
group x = new group();
Guid newGroupId = Guid.NewGuid();
x.groupId = newGroupId;
var userQuery = myDB.Where(n => n.myField == myConditon).Select(n => new
{
n.userId,//I'm assuming that the database query returns a field userId
n.userName//I'm assuming that the database query returns a field userName
});
foreach(var user in userQuery)
{
userGroup.user y = new userGroup.user();
y.groupId = newGroupId;
y.userId = user.userId;
y.userName = user.userName;
x.usersInGroup.Add(user);
}
return x;
}
}
I hope that I understood your question and that my example points you in the right direction.
Best wishes,
Bill
You can pretty easily model this by introducing an abstract base type from which both types derive.
public abstract class BaseNamedEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Group : BaseNamedEntity
{
public virtual ICollection<User> Users { get; set; }
}
public class User : BaseNamedEntity
{
public Guid GroupId { get; set; }
public virtual Group Group { get; set; }
}
In the context you tell that IsGroup is the discriminator:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BaseNamedEntity>().ToTable("MyTable");
modelBuilder.Entity<Group>().Map(m => m.Requires("IsGroup")
.HasValue(true));
modelBuilder.Entity<User>().Map(m => m.Requires("IsGroup")
.HasValue(false));
}
public DbSet<BaseNamedEntity> BaseNamedEntities { get; set; }
}
I don't think that BaseNamedEntity is a particularly good name, so maybe you have to come up with something more meaningful.

Entity Framework: Incorrect data while using Many-to-many relationship

I have following C# code that uses Entity Framework Code First approach. The tables are created in database; but the data entered is incorrect.
Person 1 is member of Club 1 and Club 3.
Person 2 is member of Club 2 and Club 3
That means Club 2 has only one member.
But using the following query it can be seen that the data reached in database is incorrect.
What change need to be done in C# code in order to make it correct?
static void Main(string[] args)
{
Database.SetInitializer<NerdDinners>(new MyInitializer());
string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";
using (var db = new NerdDinners(connectionstring))
{
Club club1 = new Club();
Club club2 = new Club();
Club club3 = new Club();
Person p1 = new Person();
Person p2 = new Person();
List<Club> clubsForPerson1 = new List<Club>();
clubsForPerson1.Add(club1);
clubsForPerson1.Add(club3);
List<Club> clubsForPerson2 = new List<Club>();
clubsForPerson2.Add(club2);
clubsForPerson2.Add(club3);
List<Person> personInClub1 = new List<Person>();
personInClub1.Add(p1);
List<Person> personInClub2 = new List<Person>();
personInClub2.Add(p2);
List<Person> personInClub3 = new List<Person>();
personInClub3.Add(p1);
personInClub3.Add(p2);
club1.Members=personInClub1;
club2.Members=personInClub2;
club3.Members=personInClub3;
p1.Clubs = clubsForPerson1;
p2.Clubs = clubsForPerson2;
db.Clubs.Add(club1);
db.Clubs.Add(club2);
db.Clubs.Add(club3);
db.Persons.Add(p1);
db.Persons.Add(p2);
int recordsAffected = db.SaveChanges();
}
}
namespace LijosEF
{
public class Person
{
public int PersonId { get; set; }
public virtual ICollection<Club> Clubs { get; set; }
}
public class Club
{
public int ClubId { get; set; }
public virtual ICollection<Person> Members { get; set; }
}
public abstract class PaymentComponent
{
public int PaymentComponentID { get; set; }
public int MyValue { get; set; }
public abstract int GetEffectiveValue();
}
public partial class GiftCouponPayment : PaymentComponent
{
public override int GetEffectiveValue()
{
if (MyValue < 2000)
{
return 0;
}
return MyValue;
}
}
public partial class ClubCardPayment : PaymentComponent
{
public override int GetEffectiveValue()
{
return MyValue;
}
}
public partial class Payment
{
public int PaymentID { get; set; }
public List<PaymentComponent> PaymentComponents { get; set; }
public DateTime PayedTime { get; set; }
}
public class MyInitializer : CreateDatabaseIfNotExists<NerdDinners>
{
//Only one identity column can be created per table.
protected override void Seed(NerdDinners context)
{
}
}
//System.Data.Entity.DbContext is from EntityFramework.dll
public class NerdDinners : System.Data.Entity.DbContext
{
public NerdDinners(string connString): base(connString)
{
}
protected override void OnModelCreating(DbModelBuilder modelbuilder)
{
//Fluent API - Plural Removal
modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public DbSet<Person> Persons { get; set; }
public DbSet<Club> Clubs { get; set; }
}
}
There was no problem actually. I thought about it as a problem due to the order of id creation. I have got some other issues that I have posted in Entity Framework: Duplicate Records in Many-to-Many relationship.
It uses
((IObjectContextAdapter)db).ObjectContext.Attach((IEntityWithKey)entity);

Categories