Inject or Bind "Alias" in an ServiceStack entity - ormlite-servicestack

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.

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));}
}

Entity Frameworok generate guid as id and save it

I'm trying to save to my table Users let's say, string ID, string email, and string password. The problem is that ID must be a guid that I have to create it and save it and not SQL server. Any ideas how?
I searched but I only found how to make SQL server to create the guid.
First of all, tell Entity framework that you will generate the value of the primary key:
Use DatabaseGenerated Attribute
public class School
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Name {get; set;}
...
}
The None option prevents values from being generated by the database automatically in cases where they would otherwise be created.
Furthermore, consider to overwrite DbContext.SaveChanges(). In this procedure ask the ChangeTracker for all elements that are Added. Generate an Id for every Added element. It might be dangerous to let others generate an Id, because they might be adding a constant value or just an auto-increment.
Another possibility would be to generate it within the Add function, but if you do that, then users could change your generated Id. So the proper place is within SaveChanges:
public override int SaveChanges()
{
var addedElements = this.ChangeTracker.Entries
.Where(entry => entry.State == EntityState.Added);
foreach(var addedElement in addedElements)
{
// This will fail: the added element doesn't have a property Id:
addedElement.Id = GenerateId();
}
return base.SaveChanges();
}
For this you have to be certain that every added element has a property Id. The simplest way is to create an interface and let all your tables implement this interface:
public interface IID
{
string Id {get; set;}
}
public class School : IID {...}
public class Student : IID {...}
public class Teacher : IID {...}
public class DbContext
{
public DbSet<School> Schools {get; set;}
public DbSet<Student> Students{get; set;}
public DbSet<Teacher> Teachers {get; set;}
public override int SaveChanges()
{
var addedElements = this.ChangeTracker.Entries.Cast<IID>
.Where(entry => entry.State == EntityState.Added);
foreach(var addedElement in addedElements)
{
addedElement.Id = GenerateId(); // Every Added element implements IId
}
return base.SaveChanges();
}
private string GenerateId()
{
... // TODO: return unique ID, for instance a GUID
}
}

ApplicationUser has a list of ApplicationUser

I have built a new Web Application that uses the template Visual Studio provides and included MVC and Web API. The default authorization mechanism is Identity and the database interaction is done using Entity Framework with Code-first method of creating the database.
I have three requirements:
A user can have a list of Children objects
I do not want to use a "relationship" object
All users already exist on the AspNetUsers table, because they all need to be able to login, so I do not want another table to maintain user data
In theory, multiple parents could have reference to multiple children, but for this example, we will just consider it a one-to-many relationship.
In my application, I need to have an ApplicationUser have a list of ChildUsers as a collection of ApplicationUser such as shown below.
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public string ShirtSize { get; set; }
public ICollection<ApplicationUser> Children { get; set; }
}
I want these users to be accessible as shown above (a collection of ApplicationUser), not a collection of Relationship object that ties them together such as:
public class Relationship
{
public String ParentId { get;set; }
public String ChildId { get;set; }
}
Can a new table be created and exist on the database without having a code-first model for it to know how to create a relationship table?
What are available solutions to this problem?
After some research, and experimentation, I have found bits and pieces of guidance to arrive at a solution that works.
In order for an intermediate table to be created to maintain the relationship, the ApplicationDbContext OnModelCreating function needs to know what it should look like. I have told it to create a new table that is not bound to an object by using the modelBuilder shown in the code below. Unfortunately, I do not have the links to the articles that guided me to this.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base( "DefaultConnection", throwIfV1Schema: false )
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
base.OnModelCreating( modelBuilder );
modelBuilder.Entity<ApplicationUser>()
.HasMany( p => p.ChildUsers )
.WithMany()
.Map( m =>
{
m.MapLeftKey( "Father_Id" );
m.MapRightKey( "Son_Id" );
m.ToTable( "father_son_relation" );
} );
}
}
Additionally, when you need to add Children to the parent ApplicationUser, you will need to do some tweaking as you are about to insert so that it updates the database correctly. I definitely want the UserManager to do the creation of the user for me, but that means that when I go to add the user to my list of Children with the code below, it tries to add it again and throws an exception because it already exists.
var result = await UserManager.CreateAsync( user, model.Password );
var myUserId = User.Identity.GetUserId();
var users = AppDbContext.Users.Where( u => u.Id == myUserId ).Include( u => u.ChildUsers );
var u2 = users.First();
u2.ChildUsers.Add( user );
await AppDbContext.SaveChangesAsync();
After finding this question, I researched the EntityStates and found that adding the following line before calling SaveChanges resolved the exception and it no longer attempts to add it again.
AppDbContext.Entry( user ).State = EntityState.Unchanged;
TADA!!! Now to select them from the database using EF, you can then use the following code:
AppDbContext.Users.Where( u => u.Id == myUserId ).Include( u => u.Children ).First();
Since I am only getting one level of Children this will work ok, after that you risk circular references.
Comments and ideas to improve the code are welcome.

EF Core custom results from stored procedure

I'm using EF Core which I believe is also known as EF 7? Anyways, I have a stored procedure that returns custom results that will not identify with any specific table. How am I supposed to access those results and how should I call the sql command?
Normally we have to use .FromSql but that is only available on entities, eg. _context.User.FromSql(). I don't have an entity for it.
So I tried building a dbset/entity for the results, but again, there is no associated table, and there is also no "Key". How am I supposed to parse the data then of the custom results?
You can create a fake entity for the result of your stored procedure. You can set any property as the Key, even if in the results of the stored procedure the key values are not unique.
For example if you have a table like the following :
CREATE TABLE [dbo].[banana_hoard]
(
[id] INT NOT NULL PRIMARY KEY IDENTITY (1,1),
[owner] NVARCHAR(64) NOT NULL,
[bananas] BIGINT NOT NULL
)
You can have a query that does not return the row id like this :
public class Program
{
public static void Main(string[] args)
{
using (var db = new MonkeyDbContext())
{
var sp_results = db.search.FromSql(#"execute <YOUR_STORED_PROC>");
str_result = String.Join("\n", sp_results.Select(a => JsonConvert.SerializeObject(a) ));
Console.WriteLine("stored proc result :\n" + str_result);
}
}
}
public class MonkeyDbContext : DbContext
{
public DbSet<StoredProcRow> search { get; set; }
protected override void OnConfiguring (DbContextOptionsBuilder builder)
{
builder.UseSqlServer(#"Server=(localdb)\monkey_db;Database=monkey_db;Trusted_Connection=True;");
}
}
public class StoredProcRow
{
[Key]
public string Owner { get; set; }
public long Bananas { get; set; }
}

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");
});
}

Resources