Casting to objects having properties with same names - simple.data

I have a problem when i try to cast to class Foo having Bar property. Properties of the class Bar have the same names as the properties of the class Foo:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public Bar Bar { get; set; }
}
public class Bar
{
public int Id { get; set; }
public string Name { get; set; }
}
Database schema looks like this:
CREATE TABLE dbo.TB_Foo
(
foo_Id INT PRIMARY KEY
,foo_Name NVARCHAR(20)
)
GO
CREATE TABLE dbo.TB_Bar
(
bar_Id INT PRIMARY KEY
,bar_Name NVARCHAR(20)
,bar_FooId INT
,FOREIGN KEY(bar_FooId) REFERENCES dbo.TB_Foo(foo_Id)
)
GO
Sample data:
INSERT INTO dbo.TB_Foo(foo_Id, foo_Name)
VALUES (1, 'Foo1'), (2, 'Foo2'), (3, 'Foo3')
INSERT INTO dbo.TB_Bar(bar_Id, bar_Name, bar_FooId)
VALUES (1, 'Bar1', 1), (2, 'Bar2', 2), (3, 'Bar3', 3)
When i try to use Simple.Data casting to object i get exception:
"System.ArgumentException: An item with the same key has already been adde"
dynamic barAlias;
List<Foo> list = db.TB_Foo
.All()
.With(db.TB_Foo.TB_Bar.As("Bar"), out barAlias)
.Select(
// Columns for properties of class Foo
db.TB_Foo.foo_Id.As("Id"),
db.TB_Foo.foo_Name.As("Name"),
// Columns for properties of class Bar
barAlias.bar_Id.As("Id"),
barAlias.bar_Name.As("Name")
)
.ToList<Foo>();
Is there a way to achieve this? (sorry for my bad english).

The problem here is that Simple.Data uses its own internal aliases to handle the With clause. That's something I can take a look at in the next iteration, but the obvious answer for now is to rename your columns so they're not prefixed with the name of the table they're in. If the table columns have the same name as the properties, then you don't need the alias and everything will work.
What's the thinking behind the column prefixes anyway?

Related

Xamarin Linq insert/update Sqlite table record with generated column

For a table defined
public class Tbl
{
[PrimaryKey]
public int Id { get; set; }
public string Col2 { get; set; }
public string Gen { get; set; }
}
Where Gen is a Sqlite stored generated always column
on _conn.Insert(instanceOfTbl)
I get 'cannot UPDATE generated column'.
Fair enough, but other than directly executing an equivalent sql statement which ignores Gen is there a way of flagging Gen as generated (or other method) to allow the insert/update? I don't think the [Ignore] column attribute is appropriate as Gen needs to be accessible for reading. Removing the Set accessor does prevent the behaviour but results in the property always being null when read.
I'm using VS, Xamarin, Nuget sqlite-net-pcl 1.7.335 targeting Android, if relevant.
I've decided to adopt this approach for now. Credit to Jason for pointing me towards it.
Split Tbl into base class Tbl and derived class TblEx. Move the generated column property to TblEx. Hardwire TblEx to map to Sqlite table Tbl. Provide TblEx method to retrieve only properties from base class Tbl.
public class Tbl
{
[PrimaryKey]
public int Id { get; set; }
public string Col2 { get; set; }
}
[SQLite.Table("Tbl")]
public class TblEx : Tbl
{
public string Gen { get; set; }
public Tbl GetTbl() {Tbl t = new Tbl(); t.Id = Id; t.Col2 = Col2; return t;}
}
Update record with primary key id
TblEx tEx = _conn.Table<TblEx>().First(t => t.Id == id);
// Generated field is available as class is TblEx
tEx.Col2 = tEx.Gen + "something";
//Update now works as only base class Tbl without reference to generated column is passed
_conn.Update(tEx.GetTbl());
An alternative way of retrieving the base class uses JSON serialize/deserialize which has the advantage of not having to explicitly assign property values but may come with a performance cost or type compatibility limitations (I'm not sure that the latter's true as I don't know much about JSON).
using System.Text.Json;
[SQLite.Table("Tbl")]
public class TblEx : Tbl
{
public string Gen { get; set; }
public Tbl GetTbl()
{return JsonSerializer.Deserialize<Tbl>(JsonSerializer.Serialize(this));}
}

OrmLite query to select some of the columns from each of 2 joined tables

Following on from this comment, how can I do a ServiceStack OrmLite query that joins two or more tables and returns some of the columns from each of them?
Using the OrmLite Does_only_populate_Select_fields_wildcard unit test as example, I'd like to do something like this:
public class DeptEmployee
{
[PrimaryKey]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[References(typeof(Department2))]
public int DepartmentId { get; set; }
[Reference]
public Department2 Department { get; set; }
}
public class Department2
{
[PrimaryKey]
public int Id { get; set; }
public string Name { get; set; }
}
var q = db.From<DeptEmployee>()
.Join<Department2>()
.Select<DeptEmployee, Department2>((de, d2) => new[] { de.FirstName, de.LastName, d2.Name });
var results = db.Select(q);
The above does not return a list of anonymous types containing FirstName, LastName and Name, as I'd expect. It still returns a list of DeptEmployee objects (but with only FirstName and LastName populated).
An important thing to note in OrmLite is how the query is constructed and executed is independent to how the results are mapped. It doesn't matter whether the query is raw custom SQL or a Typed SQL Expression, OrmLite only looks at the dataset returned to workout how the results should be mapped.
So when use the Select<T>(SqlExpression<T>) API, OrmLite will always try to map the results into the primary SqlExpression Type in db.From<DeptEmployee>() which isn't what you want since the custom columns you've selected don't match the shape of DeptEmployee POCO.
There are a few different ways to read a custom schema which all work off the same query (as it's independent to how you chose to map the results):
var q = db.From<DeptEmployee>()
.Join<Department2>()
.Select<DeptEmployee, Department2>(
(de, d2) => new { de.FirstName, de.LastName, d2.Name });
Our recommendation, esp. for a typed code-first ORM like OrmLite is to create a Typed Custom POCO and select that, e.g:
class Custom
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Name { get; set; }
}
db.Select<Custom>(q).PrintDump();
Which will print out a nice:
[
{
FirstName: First 1,
LastName: Last 1,
Name: Dept 1
},
]
The primary benefit is that you get Typed access to your custom results in a List<Custom>.
If you don't want to create a custom Type you can Select OrmLite's Dynamic Result APIs, e.g:
If you're happy knowing the positions of the different fields you can select a List<object> which will return the selected fields in the order they were selected, e.g:
db.Select<List<object>>(q).PrintDump();
Prints:
[
[
First 1,
Last 1,
Dept 1
],
]
Otherwise if you also want the names returned you can select a string object dictionary, e.g:
db.Select<Dictionary<string,object>>(q).PrintDump();
Which prints results similar to the Custom POCO, but the names and corresponding values are maintained in a loose-typed object Dictionary:
[
{
FirstName: First 1,
LastName: Last 1,
Name: Dept 1
},
]
If you were instead only selecting 2 columns, e.g:
var q = db.From<DeptEmployee>()
.Join<Department2>()
.Select<DeptEmployee, Department2>(
(de, d2) => new { de.LastName, d2.Name });
You can make of OrmLite's convenient data access APIs which will let you select 2 columns into a Dictionary<string,string>, e.g:
db.Dictionary<string,string>(q).PrintDump();
Which prints:
{
Last 1: Dept 1,
Last 2: Dept 2,
Last 3: Dept 3
}
Notice this is very different to the string object dictionary above as it returns results in a single Dictionary<string,string> for all rows instead of List<Dictionary<string,object>>, which has a Dictionary for each row.
Likewise if you were only selecting 1 field, e.g:
var q = db.From<DeptEmployee>()
.Join<Department2>()
.Select(x => x.LastName);
Then you can select a singular column of results in a List<string>, e.g:
db.Column<string>(q).PrintDump();
Which prints:
[
Last 1,
Last 2,
Last 3
]
If you instead wanted distinct results you can return them in a HashSet<string> with:
db.ColumnDistinct<string>(q).PrintDump();
To return to the original important point, it doesn't matter how the query was constructed (which just controls the SQL that's generated), OrmLite only looks at the returned resultset to Map the results, which it tries to map to the target API that you've specified you want the results mapped into, so executing custom SQL:
db.Column<string>("SELECT LastName FROM DeptEmployee").PrintDump();
Or if you executed a Stored Procedure:
db.Column<string>("EXEC GetLastNamesFromDeptEmployees").PrintDump();
Is mapped exactly the same way if you used a typed SQL Expression, i.e. OrmLite only looks at the resultset which it maps to how you want the results returned.

Inject or Bind "Alias" in an ServiceStack entity

I have 3 tables which contains same set of columns. Do i need to create 3 entities for all the DB tables? Is there a way to avoid creating 3 entities and have only one in ServiceStack?
Yes there is one way of doing it like below
List<EntityA> list = db.SqlList<EntityA>("SELECT COL_A,COL_B FROM TableA");
Entity without Alias on Class
public class EntityA
{
[Alias("COL_A")]
public string ColumnA { get; set; }
[Alias("COL_B")]
public string ColumnB { get; set; }
}
in this way i can change the table name(TableA/TableB/TableC) provided in the Query
but I want something like injecting / passing the alias while retrieving the results from the database. I am not sure if this is possible with service stack
Edited
Let me rephrase the question Instead of returning differenct objects like EntityTableA/EntityTableB/EntityTableC as Result i want
return db.Select<GenericEntity>(w => w.OrderBy(o => o.ColumnA));
the GenericEntity can be any tables result
You can just use inheritance to reduce boilerplate:
public class EntityBase
{
[Alias("COL_A")]
public string ColumnA { get; set; }
[Alias("COL_B")]
public string ColumnB { get; set; }
}
Then inherit properties from the shared entity, e.g:
public class TableA : EntityBase {}
public class TableB : EntityBase {}
Then query it as normal:
var results = db.Select<TableA>(q => ColumnA == "A");
Otherwise yeah the using any of the raw SQL API's will work as well.
Modifying SqlExpression
You can also override the SqlExpression FromExpression to include your own table, e.g:
var q = db.From<GenericEntity>().OrderBy(o => o.ColumnA);
q.From("TableA");
List<GenericEntity> results = db.Select(q);
This will change the SQL to SELECT from TableA instead.

How do I name a many-many table for EF6 and do I need to add special mapping for this?

I am using EF 6 and trying to map a many to many relationship. So far I have:
public partial class ObjectiveDetail
{
public ObjectiveDetail()
{
this.SubTopics = new List<SubTopic>();
}
public int ObjectiveDetailId { get; set; }
public string Text { get; set; }
public virtual ICollection<SubTopic> SubTopics { get; set; }
}
public partial class SubTopic
{
public SubTopic()
{
this.ObjectiveDetails = new List<ObjectiveDetail>();
}
public int SubTopicId { get; set; }
public int Number { get; set; }
public string Name { get; set; }
public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
}
Our DBA is going to write the code for the many to many table. Should this be as follows
with a table name of ObjectiveDetailSubTopic or something completely different ?
CREATE TABLE [dbo].[ObjectiveDetailSubTopic] (
[ObjectiveDetailId] INT NOT NULL,
[SubTopicId] INT NOT NULL
);
Can someone tell me if this is the correct way to create the table. Also do I have to
add some code to map the ObjectiveDetail and SubTopic classes to the new join class so
EF will know what to do?
Our DBA is going to write the code for the many to many table. Should
this be as follows with a table name of ObjectiveDetailSubTopic or
something completely different ?
As long as you follow the SQL Database table naming conventions, the table name can be anything. I usually name the join table like yours, by connecting the two table names.
To create the join table using sql, see below:
CREATE TABLE [dbo].[ObjectiveDetailSubTopic](
ObjectiveDetailSubTopicId int identity primary key,
ObjectiveDetailId INT NOT NULL,
SubTopicId INT NOT NULL,
foreign key(ObjectiveDetailId) references ObjectiveDetail(ObjectiveDetailId ),
foreign key(SubTopicId) references SubTopic(SubTopicId )
);
But you don't need to create the join table by your own, Entity Framework will create it for you. You just need to mapping the relationship with the Fluent API in your DbContext class like below:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ObjectiveDetail>().
HasMany(c => c.SubTopics).
WithMany(p => p.ObjectiveDetails).
Map(m =>
{
m.MapLeftKey("ObjectiveDetailId ");
m.MapRightKey("SubTopicId ");
m.ToTable("ObjectiveDetailSubTopic");
});
}

EF Migrations: Move Table from 2 Column PK to Single Column causes ALTER before DROP and fails

I'm using EF 4.3.1 Code First Migrations. I have a table like:
public class Product
{
[Key]
[Column(Order=0)]
[MaxLength(100)]
public string Store { get; set; }
[Key]
[Column(Order=1)]
[MaxLength(100)]
public string Sku { get; set; }
}​
I have an existing table created with the above code. I then moved it to a single-column Primary Key:
public class Product
{
[MaxLength(100)]
public string Store { get; set; }
[Key]
[MaxLength(100)]
public string Sku { get; set; }
}​
This causes EF to fail in the next automatic migration, complaining:
ALTER TABLE [Product] ALTER COLUMN [Store] nvarchar
The object 'PK_Product' is dependent on column 'Store'. ALTER
TABLE ALTER COLUMN Store failed because one or more objects access this
column.
Clearly the PK_Product needs to be dropped before attempting to fire this ALTER statement (why is it altering the column at all?), but instead the migration fails.
Am I doing something wrong or is this a bug? Workarounds?
You won't be able to do this with an automatic migration. You'll have to create a migration using Add-Migration and then change it so it only modifies the PK.
The migration can be as simple as:
public partial class TheMigration : DbMigration
{
public override void Up()
{
DropPrimaryKey("Products", new[] { "Store", "Sku" });
AddPrimaryKey("Products", "Sku");
}
public override void Down()
{
DropPrimaryKey("Products", new[] { "Sku" });
AddPrimaryKey("Products", new[] { "Store", "Sku" });
}
}
EF is altering the column because, when it's part of a Key, it's implicitly NOT NULL.
You can leave it as-is, add a [Required] attribute, or allow EF to alter the column after dropping the PK.

Resources